class RoutePreloader

Same name in other branches
  1. 8.9.x core/lib/Drupal/Core/Routing/RoutePreloader.php \Drupal\Core\Routing\RoutePreloader
  2. 10 core/lib/Drupal/Core/Routing/RoutePreloader.php \Drupal\Core\Routing\RoutePreloader
  3. 11.x core/lib/Drupal/Core/Routing/RoutePreloader.php \Drupal\Core\Routing\RoutePreloader

Defines a class which preloads non-admin routes.

On an actual site we want to avoid too many database queries so we build a list of all routes which most likely appear on the actual site, which are all HTML routes not starting with "/admin".

Hierarchy

  • class \Drupal\Core\Routing\RoutePreloader implements \Symfony\Component\EventDispatcher\EventSubscriberInterface

Expanded class hierarchy of RoutePreloader

1 file declares its use of RoutePreloader
RoutePreloaderTest.php in core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php
1 string reference to 'RoutePreloader'
core.services.yml in core/core.services.yml
core/core.services.yml
1 service uses RoutePreloader
router.route_preloader in core/core.services.yml
Drupal\Core\Routing\RoutePreloader

File

core/lib/Drupal/Core/Routing/RoutePreloader.php, line 20

Namespace

Drupal\Core\Routing
View source
class RoutePreloader implements EventSubscriberInterface {
    
    /**
     * The route provider.
     *
     * @var \Drupal\Core\Routing\RouteProviderInterface|\Drupal\Core\Routing\PreloadableRouteProviderInterface
     */
    protected $routeProvider;
    
    /**
     * The state key value store.
     *
     * @var \Drupal\Core\State\StateInterface
     */
    protected $state;
    
    /**
     * Contains the non-admin routes while rebuilding the routes.
     *
     * @var array
     */
    protected $nonAdminRoutesOnRebuild = [];
    
    /**
     * 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
     *   The cache backend.
     */
    public function __construct(RouteProviderInterface $route_provider, StateInterface $state, CacheBackendInterface $cache) {
        $this->routeProvider = $route_provider;
        $this->state = $state;
        $this->cache = $cache;
    }
    
    /**
     * Loads all non-admin routes right before the actual page is rendered.
     *
     * @param \Symfony\Component\HttpKernel\Event\KernelEvent $event
     *   The event to process.
     */
    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') {
            // 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, []);
                $this->cache
                    ->set($key, $routes, Cache::PERMANENT, [
                    'routes',
                ]);
            }
            if ($routes) {
                // Preload all the non-admin routes at once.
                $this->routeProvider
                    ->preLoadRoutes($routes);
            }
        }
    }
    
    /**
     * Alters existing routes for a specific collection.
     *
     * @param \Drupal\Core\Routing\RouteBuildEvent $event
     *   The route build event.
     */
    public function onAlterRoutes(RouteBuildEvent $event) {
        $collection = $event->getRouteCollection();
        foreach ($collection->all() as $name => $route) {
            if (strpos($route->getPath(), '/admin/') !== 0 && $route->getPath() != '/admin' && static::isGetAndHtmlRoute($route)) {
                $this->nonAdminRoutesOnRebuild[] = $name;
            }
        }
        $this->nonAdminRoutesOnRebuild = array_unique($this->nonAdminRoutesOnRebuild);
    }
    
    /**
     * Store the non admin routes in state when the route building is finished.
     */
    public function onFinishedRoutes() {
        $this->state
            ->set('routing.non_admin_routes', $this->nonAdminRoutesOnRebuild);
        $this->nonAdminRoutesOnRebuild = [];
    }
    
    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents() {
        // Set a really low priority to catch as many as possible routes.
        $events[RoutingEvents::ALTER] = [
            'onAlterRoutes',
            -1024,
        ];
        $events[RoutingEvents::FINISHED] = [
            'onFinishedRoutes',
        ];
        // Load the routes before the controller is executed (which happens after
        // the kernel request event).
        $events[KernelEvents::REQUEST][] = [
            'onRequest',
        ];
        return $events;
    }
    
    /**
     * Determines whether the given route is a GET and HTML route.
     *
     * @param \Symfony\Component\Routing\Route $route
     *   The route to analyze.
     *
     * @return bool
     *   TRUE if GET is a valid method and HTML is a valid format for this route.
     */
    protected static function isGetAndHtmlRoute(Route $route) {
        $methods = $route->getMethods() ?: [
            'GET',
        ];
        // If a route has no explicit format, then HTML is valid.
        // @see \Drupal\Core\Routing\RequestFormatRouteFilter::getAvailableFormats()
        $format = $route->hasRequirement('_format') ? explode('|', $route->getRequirement('_format')) : [
            'html',
        ];
        return in_array('GET', $methods, TRUE) && in_array('html', $format, TRUE);
    }

}

Members

Title Sort descending Modifiers Object type Summary
RoutePreloader::$cache protected property The cache backend used to skip the state loading.
RoutePreloader::$nonAdminRoutesOnRebuild protected property Contains the non-admin routes while rebuilding the routes.
RoutePreloader::$routeProvider protected property The route provider.
RoutePreloader::$state protected property The state key value store.
RoutePreloader::getSubscribedEvents public static function
RoutePreloader::isGetAndHtmlRoute protected static function Determines whether the given route is a GET and HTML route.
RoutePreloader::onAlterRoutes public function Alters existing routes for a specific collection.
RoutePreloader::onFinishedRoutes public function Store the non admin routes in state when the route building is finished.
RoutePreloader::onRequest public function Loads all non-admin routes right before the actual page is rendered.
RoutePreloader::__construct public function Constructs a new RoutePreloader.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.