diff --git a/core/lib/Drupal/Core/Routing/LazyLoadingRouteCollection.php b/core/lib/Drupal/Core/Routing/LazyLoadingRouteCollection.php new file mode 100644 index 00000000000..3929cabe677 --- /dev/null +++ b/core/lib/Drupal/Core/Routing/LazyLoadingRouteCollection.php @@ -0,0 +1,147 @@ +database = $database; + $this->tableName = $table; + } + + /** + * Loads the next routes into the elements array. + * + * @param int $offset + * The offset used in the db query. + */ + protected function loadNextElements($offset) { + $this->elements = array(); + + $query = $this->database->select($this->tableName); + $query->addField($this->tableName, 'name'); + $query->addField($this->tableName, 'route'); + $query->orderBy('name', 'ASC'); + $query->range($offset, static::ROUTE_LOADED_PER_TIME); + $result = $query->execute()->fetchAllKeyed(); + + $routes = array(); + foreach ($result as $name => $route) { + $routes[$name] = unserialize($route); + } + $this->elements = $routes; + } + + /** + * {inheritdoc} + */ + public function count() { + if (!isset($this->count)) { + $this->count = (int) $this->database->select($this->tableName)->countQuery()->execute(); + } + return $this->count; + } + + /** + * {@inheritdoc} + */ + public function current() { + return current($this->elements); + } + + /** + * {@inheritdoc} + */ + public function next() { + $result = next($this->elements); + if ($result === FALSE) { + $this->loadNextElements($this->currentRoute + 1); + } + $this->currentRoute++; + } + + /** + * {@inheritdoc} + */ + public function key() { + return key($this->elements); + } + + /** + * {@inheritdoc} + */ + public function valid() { + return key($this->elements); + } + + /** + * {@inheritdoc} + */ + public function rewind() { + $this->currentRoute = 0; + $this->loadNextElements($this->currentRoute); + } + +} diff --git a/core/lib/Drupal/Core/Routing/RouteProvider.php b/core/lib/Drupal/Core/Routing/RouteProvider.php index 419cc75a4b8..e90aea3fec4 100644 --- a/core/lib/Drupal/Core/Routing/RouteProvider.php +++ b/core/lib/Drupal/Core/Routing/RouteProvider.php @@ -262,4 +262,11 @@ class RouteProvider implements RouteProviderInterface { return $collection; } + /** + * {@inheritdoc} + */ + public function getAllRoutes() { + return new LazyLoadingRouteCollection($this->connection, $this->tableName); + } + } diff --git a/core/lib/Drupal/Core/Routing/RouteProviderInterface.php b/core/lib/Drupal/Core/Routing/RouteProviderInterface.php index aa0375f9dd0..f84a19ba26e 100644 --- a/core/lib/Drupal/Core/Routing/RouteProviderInterface.php +++ b/core/lib/Drupal/Core/Routing/RouteProviderInterface.php @@ -28,4 +28,16 @@ interface RouteProviderInterface extends RouteProviderBaseInterface { */ public function getRoutesByPattern($pattern); + /** + * Returns all the routes on the system. + * + * Usage of this method is discouraged for performance reasons. If possible, + * use RouteProviderInterface::getRoutesByNames() or + * RouteProviderInterface::getRoutesByPattern() instead. + * + * @return \Symfony\Component\Routing\Route[] + * An iterator of routes keyed by route name. + */ + public function getAllRoutes(); + } diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MockRouteProvider.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MockRouteProvider.php index e0ed543f595..de56a90d178 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/MockRouteProvider.php +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/MockRouteProvider.php @@ -75,5 +75,11 @@ class MockRouteProvider implements RouteProviderInterface { return new RouteCollection(); } + /** + * @inheritdoc} + */ + public function getAllRoutes() { + return $this->routes->all(); + } } diff --git a/core/tests/Drupal/Tests/Core/Routing/LazyLoadingRouteCollectionTest.php b/core/tests/Drupal/Tests/Core/Routing/LazyLoadingRouteCollectionTest.php new file mode 100644 index 00000000000..749ba882da7 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Routing/LazyLoadingRouteCollectionTest.php @@ -0,0 +1,113 @@ + 'Lazy loaded route collection', + 'description' => 'Tests the lazy loaded route collection.', + 'group' => 'Routing', + ); + } + + protected function setUp() { + for ($i = 0; $i < 20; $i++) { + $this->routes['test_route_' . $i] = new Route('/test-route-' . $i); + } + + $this->routeCollection = new TestRouteCollection($this->routes); + } + + /** + * Tests iterating the lazy loading route collection. + * + * @see \Drupal\Core\Routing\LazyLoadingRouteCollection::current() + * @see \Drupal\Core\Routing\LazyLoadingRouteCollection::key() + * @see \Drupal\Core\Routing\LazyLoadingRouteCollection::rewind() + */ + public function testIterating() { + // Execute the foreach loop twice to ensure that rewind is called. + for ($i = 0; $i < 2; $i++) { + $route_names = array_keys($this->routes); + $count = 0; + foreach ($this->routeCollection as $route_name => $route) { + $this->assertEquals($route_names[$count], $route_name); + $this->assertEquals($this->routes[$route_names[$count]], $route); + + $count++; + } + } + } + +} + +/** + * Wrapper class to "inject" loaded routes. + */ +class TestRouteCollection extends LazyLoadingRouteCollection { + + /** + * {@inheritdoc} + */ + const ROUTE_LOADED_PER_TIME = 2; + + /** + * Stores all elements. + * + * @var \Symfony\Component\Routing\Route[] + */ + protected $allRoutes; + + /** + * Creates a TestCollection instance. + * + * @param \Symfony\Component\Routing\Route[] $all_routes + * Contains all the routes used in the test. + */ + public function __construct(array $all_routes) { + $this->allRoutes = $all_routes; + $this->loadNextElements($this->currentRoute); + } + + /** + * {@inheritdoc} + */ + protected function loadNextElements($offset) { + $elements = array_slice($this->allRoutes, $offset, static::ROUTE_LOADED_PER_TIME); + + $this->elements = $elements; + } + +}