annotate core/modules/views/src/EventSubscriber/RouteSubscriber.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\views\EventSubscriber;
Chris@0 4
Chris@18 5 use Drupal\Core\Entity\EntityTypeManagerInterface;
Chris@0 6 use Drupal\Core\State\StateInterface;
Chris@0 7 use Drupal\Core\Routing\RouteSubscriberBase;
Chris@0 8 use Drupal\Core\Routing\RoutingEvents;
Chris@0 9 use Drupal\views\Plugin\views\display\DisplayRouterInterface;
Chris@0 10 use Drupal\views\ViewExecutable;
Chris@0 11 use Drupal\views\Views;
Chris@0 12 use Symfony\Component\Routing\RouteCollection;
Chris@0 13
Chris@0 14 /**
Chris@0 15 * Builds up the routes of all views.
Chris@0 16 *
Chris@0 17 * The general idea is to execute first all alter hooks to determine which
Chris@0 18 * routes are overridden by views. This information is used to determine which
Chris@0 19 * views have to be added by views in the dynamic event.
Chris@0 20 *
Chris@0 21 *
Chris@0 22 * @see \Drupal\views\Plugin\views\display\PathPluginBase
Chris@0 23 */
Chris@0 24 class RouteSubscriber extends RouteSubscriberBase {
Chris@0 25
Chris@0 26 /**
Chris@0 27 * Stores a list of view,display IDs which haven't be used in the alter event.
Chris@0 28 *
Chris@0 29 * @var array
Chris@0 30 */
Chris@0 31 protected $viewsDisplayPairs;
Chris@0 32
Chris@0 33 /**
Chris@0 34 * The view storage.
Chris@0 35 *
Chris@0 36 * @var \Drupal\Core\Entity\EntityStorageInterface
Chris@0 37 */
Chris@0 38 protected $viewStorage;
Chris@0 39
Chris@0 40 /**
Chris@0 41 * The state key value store.
Chris@0 42 *
Chris@0 43 * @var \Drupal\Core\State\StateInterface
Chris@0 44 */
Chris@0 45 protected $state;
Chris@0 46
Chris@0 47 /**
Chris@0 48 * Stores an array of route names keyed by view_id.display_id.
Chris@0 49 *
Chris@0 50 * @var array
Chris@0 51 */
Chris@0 52 protected $viewRouteNames = [];
Chris@0 53
Chris@0 54 /**
Chris@0 55 * Constructs a \Drupal\views\EventSubscriber\RouteSubscriber instance.
Chris@0 56 *
Chris@18 57 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
Chris@18 58 * The entity type manager service.
Chris@0 59 * @param \Drupal\Core\State\StateInterface $state
Chris@0 60 * The state key value store.
Chris@0 61 */
Chris@18 62 public function __construct(EntityTypeManagerInterface $entity_type_manager, StateInterface $state) {
Chris@18 63 $this->viewStorage = $entity_type_manager->getStorage('view');
Chris@0 64 $this->state = $state;
Chris@0 65 }
Chris@0 66
Chris@0 67 /**
Chris@0 68 * Resets the internal state of the route subscriber.
Chris@0 69 */
Chris@0 70 public function reset() {
Chris@0 71 $this->viewsDisplayPairs = NULL;
Chris@0 72 }
Chris@0 73
Chris@0 74 /**
Chris@0 75 * {@inheritdoc}
Chris@0 76 */
Chris@0 77 public static function getSubscribedEvents() {
Chris@0 78 $events = parent::getSubscribedEvents();
Chris@0 79 $events[RoutingEvents::FINISHED] = ['routeRebuildFinished'];
Chris@0 80 // Ensure to run after the entity resolver subscriber
Chris@0 81 // @see \Drupal\Core\EventSubscriber\EntityRouteAlterSubscriber
Chris@0 82 $events[RoutingEvents::ALTER] = ['onAlterRoutes', -175];
Chris@0 83
Chris@0 84 return $events;
Chris@0 85 }
Chris@0 86
Chris@0 87 /**
Chris@0 88 * Gets all the views and display IDs using a route.
Chris@0 89 */
Chris@0 90 protected function getViewsDisplayIDsWithRoute() {
Chris@0 91 if (!isset($this->viewsDisplayPairs)) {
Chris@0 92 $this->viewsDisplayPairs = [];
Chris@0 93
Chris@0 94 // @todo Convert this method to some service.
Chris@0 95 $views = $this->getApplicableViews();
Chris@0 96 foreach ($views as $data) {
Chris@0 97 list($view_id, $display_id) = $data;
Chris@0 98 $this->viewsDisplayPairs[] = $view_id . '.' . $display_id;
Chris@0 99 }
Chris@0 100 $this->viewsDisplayPairs = array_combine($this->viewsDisplayPairs, $this->viewsDisplayPairs);
Chris@0 101 }
Chris@0 102 return $this->viewsDisplayPairs;
Chris@0 103 }
Chris@0 104
Chris@0 105 /**
Chris@0 106 * Returns a set of route objects.
Chris@0 107 *
Chris@0 108 * @return \Symfony\Component\Routing\RouteCollection
Chris@0 109 * A route collection.
Chris@0 110 */
Chris@0 111 public function routes() {
Chris@0 112 $collection = new RouteCollection();
Chris@0 113 foreach ($this->getViewsDisplayIDsWithRoute() as $pair) {
Chris@0 114 list($view_id, $display_id) = explode('.', $pair);
Chris@0 115 $view = $this->viewStorage->load($view_id);
Chris@0 116 // @todo This should have an executable factory injected.
Chris@0 117 if (($view = $view->getExecutable()) && $view instanceof ViewExecutable) {
Chris@0 118 if ($view->setDisplay($display_id) && $display = $view->displayHandlers->get($display_id)) {
Chris@0 119 if ($display instanceof DisplayRouterInterface) {
Chris@0 120 $this->viewRouteNames += (array) $display->collectRoutes($collection);
Chris@0 121 }
Chris@0 122 }
Chris@0 123 $view->destroy();
Chris@0 124 }
Chris@0 125 }
Chris@0 126
Chris@0 127 $this->state->set('views.view_route_names', $this->viewRouteNames);
Chris@0 128 return $collection;
Chris@0 129 }
Chris@0 130
Chris@0 131 /**
Chris@0 132 * {@inheritdoc}
Chris@0 133 */
Chris@0 134 protected function alterRoutes(RouteCollection $collection) {
Chris@0 135 foreach ($this->getViewsDisplayIDsWithRoute() as $pair) {
Chris@0 136 list($view_id, $display_id) = explode('.', $pair);
Chris@0 137 $view = $this->viewStorage->load($view_id);
Chris@0 138 // @todo This should have an executable factory injected.
Chris@0 139 if (($view = $view->getExecutable()) && $view instanceof ViewExecutable) {
Chris@0 140 if ($view->setDisplay($display_id) && $display = $view->displayHandlers->get($display_id)) {
Chris@0 141 if ($display instanceof DisplayRouterInterface) {
Chris@0 142 // If the display returns TRUE a route item was found, so it does not
Chris@0 143 // have to be added.
Chris@0 144 $view_route_names = $display->alterRoutes($collection);
Chris@0 145 $this->viewRouteNames = $view_route_names + $this->viewRouteNames;
Chris@0 146 foreach ($view_route_names as $id_display => $route_name) {
Chris@0 147 $view_route_name = $this->viewsDisplayPairs[$id_display];
Chris@0 148 unset($this->viewsDisplayPairs[$id_display]);
Chris@0 149 $collection->remove("views.$view_route_name");
Chris@0 150 }
Chris@0 151 }
Chris@0 152 }
Chris@0 153 $view->destroy();
Chris@0 154 }
Chris@0 155 }
Chris@0 156 }
Chris@0 157
Chris@0 158 /**
Chris@0 159 * Stores the new route names after they have been rebuilt.
Chris@0 160 *
Chris@0 161 * Callback for the RoutingEvents::FINISHED event.
Chris@0 162 *
Chris@0 163 * @see \Drupal\views\EventSubscriber::getSubscribedEvents()
Chris@0 164 */
Chris@0 165 public function routeRebuildFinished() {
Chris@0 166 $this->reset();
Chris@0 167 $this->state->set('views.view_route_names', $this->viewRouteNames);
Chris@0 168 }
Chris@0 169
Chris@0 170 /**
Chris@0 171 * Returns all views/display combinations with routes.
Chris@0 172 *
Chris@0 173 * @see \Drupal\views\Views::getApplicableViews()
Chris@0 174 */
Chris@0 175 protected function getApplicableViews() {
Chris@0 176 return Views::getApplicableViews('uses_route');
Chris@0 177 }
Chris@0 178
Chris@0 179 }