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 }
|