diff --git a/core/core.services.yml b/core/core.services.yml index cd817bc..d137618 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -659,7 +659,7 @@ services: - { name: backend_overridable } router.route_preloader: class: Drupal\Core\Routing\RoutePreloader - arguments: ['@router.route_provider', '@state'] + arguments: ['@router.route_provider', '@state', '@cache.bootstrap'] tags: - { name: 'event_subscriber' } router.matcher.final_matcher: diff --git a/core/lib/Drupal/Core/Routing/RoutePreloader.php b/core/lib/Drupal/Core/Routing/RoutePreloader.php index 23a09aa..db1dc95 100644 --- a/core/lib/Drupal/Core/Routing/RoutePreloader.php +++ b/core/lib/Drupal/Core/Routing/RoutePreloader.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Routing; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\State\StateInterface; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -44,16 +45,25 @@ class RoutePreloader implements EventSubscriberInterface { protected $nonAdminRoutesOnRebuild = array(); /** + * The cache backend used to skip the state loading. + * + * @var \Drupal\Core\Cache\CacheBackendInterface + */ + protected $cache; + + /** * Constructs a new RoutePreloader. * * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider * The route provider. * @param \Drupal\Core\State\StateInterface $state * The state key value store. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache */ - public function __construct(RouteProviderInterface $route_provider, StateInterface $state) { + public function __construct(RouteProviderInterface $route_provider, StateInterface $state, CacheBackendInterface $cache) { $this->routeProvider = $route_provider; $this->state = $state; + $this->cache = $cache; } /** @@ -65,7 +75,18 @@ public function __construct(RouteProviderInterface $route_provider, StateInterfa public function onRequest(KernelEvent $event) { // Only preload on normal HTML pages, as they will display menu links. if ($this->routeProvider instanceof PreloadableRouteProviderInterface && $event->getRequest()->getRequestFormat() == 'html') { - if ($routes = $this->state->get('routing.non_admin_routes', [])) { + + // Ensure that the state query is cached to skip the database query, if + // possible. + $key = 'routing.non_admin_routes'; + if ($cache = $this->cache->get($key)) { + $routes = $cache->data; + } + else { + $routes = $this->state->get($key, []); + } + + if ($routes) { // Preload all the non-admin routes at once. $this->routeProvider->preLoadRoutes($routes); } diff --git a/core/lib/Drupal/Core/Routing/RouteProvider.php b/core/lib/Drupal/Core/Routing/RouteProvider.php index 170c10c..f79b4e7 100644 --- a/core/lib/Drupal/Core/Routing/RouteProvider.php +++ b/core/lib/Drupal/Core/Routing/RouteProvider.php @@ -83,6 +83,11 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv protected $pathProcessor; /** + * Cache ID prefix used to load routes. + */ + const ROUTE_LOAD_CID = 'route_provider.route_load:'; + + /** * Constructs a new PathMatcher. * * @param \Drupal\Core\Database\Connection $connection @@ -187,8 +192,15 @@ public function preLoadRoutes($names) { $routes_to_load = array_diff($names, array_keys($this->routes), array_keys($this->serializedRoutes)); if ($routes_to_load) { - $result = $this->connection->query('SELECT name, route FROM {' . $this->connection->escapeTable($this->tableName) . '} WHERE name IN ( :names[] )', array(':names[]' => $routes_to_load)); - $routes = $result->fetchAllKeyed(); + + if ($cache = $this->cache->get(static::ROUTE_LOAD_CID . hash('sha512', serialize($routes_to_load)))) { + $routes = $cache->data; + } + else { + $result = $this->connection->query('SELECT name, route FROM {' . $this->connection->escapeTable($this->tableName) . '} WHERE name IN ( :names[] )', array(':names[]' => $routes_to_load)); + $routes = $result->fetchAllKeyed(); + } + $this->serializedRoutes += $routes; } }