annotate core/lib/Drupal/Core/Routing/RoutePreloader.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core\Routing;
Chris@0 4
Chris@0 5 use Drupal\Core\Cache\Cache;
Chris@0 6 use Drupal\Core\Cache\CacheBackendInterface;
Chris@0 7 use Drupal\Core\State\StateInterface;
Chris@0 8 use Symfony\Component\EventDispatcher\Event;
Chris@0 9 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
Chris@0 10 use Symfony\Component\HttpKernel\Event\KernelEvent;
Chris@0 11 use Symfony\Component\HttpKernel\KernelEvents;
Chris@0 12
Chris@0 13 /**
Chris@0 14 * Defines a class which preloads non-admin routes.
Chris@0 15 *
Chris@0 16 * On an actual site we want to avoid too many database queries so we build a
Chris@0 17 * list of all routes which most likely appear on the actual site, which are all
Chris@0 18 * HTML routes not starting with "/admin".
Chris@0 19 */
Chris@0 20 class RoutePreloader implements EventSubscriberInterface {
Chris@0 21
Chris@0 22 /**
Chris@0 23 * The route provider.
Chris@0 24 *
Chris@0 25 * @var \Drupal\Core\Routing\RouteProviderInterface|\Drupal\Core\Routing\PreloadableRouteProviderInterface
Chris@0 26 */
Chris@0 27 protected $routeProvider;
Chris@0 28
Chris@0 29 /**
Chris@0 30 * The state key value store.
Chris@0 31 *
Chris@0 32 * @var \Drupal\Core\State\StateInterface
Chris@0 33 */
Chris@0 34 protected $state;
Chris@0 35
Chris@0 36 /**
Chris@0 37 * Contains the non-admin routes while rebuilding the routes.
Chris@0 38 *
Chris@0 39 * @var array
Chris@0 40 */
Chris@0 41 protected $nonAdminRoutesOnRebuild = [];
Chris@0 42
Chris@0 43 /**
Chris@0 44 * The cache backend used to skip the state loading.
Chris@0 45 *
Chris@0 46 * @var \Drupal\Core\Cache\CacheBackendInterface
Chris@0 47 */
Chris@0 48 protected $cache;
Chris@0 49
Chris@0 50 /**
Chris@0 51 * Constructs a new RoutePreloader.
Chris@0 52 *
Chris@0 53 * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
Chris@0 54 * The route provider.
Chris@0 55 * @param \Drupal\Core\State\StateInterface $state
Chris@0 56 * The state key value store.
Chris@0 57 * @param \Drupal\Core\Cache\CacheBackendInterface $cache
Chris@0 58 */
Chris@0 59 public function __construct(RouteProviderInterface $route_provider, StateInterface $state, CacheBackendInterface $cache) {
Chris@0 60 $this->routeProvider = $route_provider;
Chris@0 61 $this->state = $state;
Chris@0 62 $this->cache = $cache;
Chris@0 63 }
Chris@0 64
Chris@0 65 /**
Chris@0 66 * Loads all non-admin routes right before the actual page is rendered.
Chris@0 67 *
Chris@0 68 * @param \Symfony\Component\HttpKernel\Event\KernelEvent $event
Chris@0 69 * The event to process.
Chris@0 70 */
Chris@0 71 public function onRequest(KernelEvent $event) {
Chris@0 72 // Only preload on normal HTML pages, as they will display menu links.
Chris@0 73 if ($this->routeProvider instanceof PreloadableRouteProviderInterface && $event->getRequest()->getRequestFormat() == 'html') {
Chris@0 74
Chris@0 75 // Ensure that the state query is cached to skip the database query, if
Chris@0 76 // possible.
Chris@0 77 $key = 'routing.non_admin_routes';
Chris@0 78 if ($cache = $this->cache->get($key)) {
Chris@0 79 $routes = $cache->data;
Chris@0 80 }
Chris@0 81 else {
Chris@0 82 $routes = $this->state->get($key, []);
Chris@0 83 $this->cache->set($key, $routes, Cache::PERMANENT, ['routes']);
Chris@0 84 }
Chris@0 85
Chris@0 86 if ($routes) {
Chris@0 87 // Preload all the non-admin routes at once.
Chris@0 88 $this->routeProvider->preLoadRoutes($routes);
Chris@0 89 }
Chris@0 90 }
Chris@0 91 }
Chris@0 92
Chris@0 93 /**
Chris@0 94 * Alters existing routes for a specific collection.
Chris@0 95 *
Chris@0 96 * @param \Drupal\Core\Routing\RouteBuildEvent $event
Chris@0 97 * The route build event.
Chris@0 98 */
Chris@0 99 public function onAlterRoutes(RouteBuildEvent $event) {
Chris@0 100 $collection = $event->getRouteCollection();
Chris@0 101 foreach ($collection->all() as $name => $route) {
Chris@0 102 if (strpos($route->getPath(), '/admin/') !== 0 && $route->getPath() != '/admin') {
Chris@0 103 $this->nonAdminRoutesOnRebuild[] = $name;
Chris@0 104 }
Chris@0 105 }
Chris@0 106 $this->nonAdminRoutesOnRebuild = array_unique($this->nonAdminRoutesOnRebuild);
Chris@0 107 }
Chris@0 108
Chris@0 109 /**
Chris@0 110 * Store the non admin routes in state when the route building is finished.
Chris@0 111 *
Chris@0 112 * @param \Symfony\Component\EventDispatcher\Event $event
Chris@0 113 * The route finish event.
Chris@0 114 */
Chris@0 115 public function onFinishedRoutes(Event $event) {
Chris@0 116 $this->state->set('routing.non_admin_routes', $this->nonAdminRoutesOnRebuild);
Chris@0 117 $this->nonAdminRoutesOnRebuild = [];
Chris@0 118 }
Chris@0 119
Chris@0 120 /**
Chris@0 121 * {@inheritdoc}
Chris@0 122 */
Chris@0 123 public static function getSubscribedEvents() {
Chris@0 124 // Set a really low priority to catch as many as possible routes.
Chris@0 125 $events[RoutingEvents::ALTER] = ['onAlterRoutes', -1024];
Chris@0 126 $events[RoutingEvents::FINISHED] = ['onFinishedRoutes'];
Chris@0 127 // Load the routes before the controller is executed (which happens after
Chris@0 128 // the kernel request event).
Chris@0 129 $events[KernelEvents::REQUEST][] = ['onRequest'];
Chris@0 130 return $events;
Chris@0 131 }
Chris@0 132
Chris@0 133 }