Version 1
[yaffs-website] / web / core / lib / Drupal / Core / Entity / Routing / DefaultHtmlRouteProvider.php
1 <?php
2
3 namespace Drupal\Core\Entity\Routing;
4
5 use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
6 use Drupal\Core\Entity\Controller\EntityController;
7 use Drupal\Core\Entity\EntityFieldManagerInterface;
8 use Drupal\Core\Entity\EntityHandlerInterface;
9 use Drupal\Core\Entity\EntityTypeInterface;
10 use Drupal\Core\Entity\EntityTypeManagerInterface;
11 use Drupal\Core\Entity\FieldableEntityInterface;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
13 use Symfony\Component\Routing\Route;
14 use Symfony\Component\Routing\RouteCollection;
15
16 /**
17  * Provides HTML routes for entities.
18  *
19  * This class provides the following routes for entities, with title and access
20  * callbacks:
21  * - canonical
22  * - add-page
23  * - add-form
24  * - edit-form
25  * - delete-form
26  * - collection
27  *
28  * @see \Drupal\Core\Entity\Routing\AdminHtmlRouteProvider.
29  */
30 class DefaultHtmlRouteProvider implements EntityRouteProviderInterface, EntityHandlerInterface {
31
32   /**
33    * The entity type manager.
34    *
35    * @var \Drupal\Core\Entity\EntityManagerInterface
36    */
37   protected $entityTypeManager;
38
39   /**
40    * The entity field manager.
41    *
42    * @var \Drupal\Core\Entity\EntityFieldManagerInterface
43    */
44   protected $entityFieldManager;
45
46   /**
47    * Constructs a new DefaultHtmlRouteProvider.
48    *
49    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
50    *   The entity type manager.
51    * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
52    *   The entity field manager.
53    */
54   public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager) {
55     $this->entityTypeManager = $entity_type_manager;
56     $this->entityFieldManager = $entity_field_manager;
57   }
58
59   /**
60    * {@inheritdoc}
61    */
62   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
63     return new static(
64       $container->get('entity_type.manager'),
65       $container->get('entity_field.manager')
66     );
67   }
68
69   /**
70    * {@inheritdoc}
71    */
72   public function getRoutes(EntityTypeInterface $entity_type) {
73     $collection = new RouteCollection();
74
75     $entity_type_id = $entity_type->id();
76
77     if ($add_page_route = $this->getAddPageRoute($entity_type)) {
78       $collection->add("entity.{$entity_type_id}.add_page", $add_page_route);
79     }
80
81     if ($add_form_route = $this->getAddFormRoute($entity_type)) {
82       $collection->add("entity.{$entity_type_id}.add_form", $add_form_route);
83     }
84
85     if ($canonical_route = $this->getCanonicalRoute($entity_type)) {
86       $collection->add("entity.{$entity_type_id}.canonical", $canonical_route);
87     }
88
89     if ($edit_route = $this->getEditFormRoute($entity_type)) {
90       $collection->add("entity.{$entity_type_id}.edit_form", $edit_route);
91     }
92
93     if ($delete_route = $this->getDeleteFormRoute($entity_type)) {
94       $collection->add("entity.{$entity_type_id}.delete_form", $delete_route);
95     }
96
97     if ($collection_route = $this->getCollectionRoute($entity_type)) {
98       $collection->add("entity.{$entity_type_id}.collection", $collection_route);
99     }
100
101     return $collection;
102   }
103
104   /**
105    * Gets the add page route.
106    *
107    * Built only for entity types that have bundles.
108    *
109    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
110    *   The entity type.
111    *
112    * @return \Symfony\Component\Routing\Route|null
113    *   The generated route, if available.
114    */
115   protected function getAddPageRoute(EntityTypeInterface $entity_type) {
116     if ($entity_type->hasLinkTemplate('add-page') && $entity_type->getKey('bundle')) {
117       $route = new Route($entity_type->getLinkTemplate('add-page'));
118       $route->setDefault('_controller', EntityController::class . '::addPage');
119       $route->setDefault('_title_callback', EntityController::class . '::addTitle');
120       $route->setDefault('entity_type_id', $entity_type->id());
121       $route->setRequirement('_entity_create_any_access', $entity_type->id());
122
123       return $route;
124     }
125   }
126
127   /**
128    * Gets the add-form route.
129    *
130    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
131    *   The entity type.
132    *
133    * @return \Symfony\Component\Routing\Route|null
134    *   The generated route, if available.
135    */
136   protected function getAddFormRoute(EntityTypeInterface $entity_type) {
137     if ($entity_type->hasLinkTemplate('add-form')) {
138       $entity_type_id = $entity_type->id();
139       $route = new Route($entity_type->getLinkTemplate('add-form'));
140       // Use the add form handler, if available, otherwise default.
141       $operation = 'default';
142       if ($entity_type->getFormClass('add')) {
143         $operation = 'add';
144       }
145       $route->setDefaults([
146         '_entity_form' => "{$entity_type_id}.{$operation}",
147         'entity_type_id' => $entity_type_id,
148       ]);
149
150       // If the entity has bundles, we can provide a bundle-specific title
151       // and access requirements.
152       $expected_parameter = $entity_type->getBundleEntityType() ?: $entity_type->getKey('bundle');
153       // @todo: We have to check if a route contains a bundle in its path as
154       //   test entities have inconsistent usage of "add-form" link templates.
155       //   Fix it in https://www.drupal.org/node/2699959.
156       if (($bundle_key = $entity_type->getKey('bundle')) && strpos($route->getPath(), '{' . $expected_parameter . '}') !== FALSE) {
157         $route->setDefault('_title_callback', EntityController::class . '::addBundleTitle');
158         // If the bundles are entities themselves, we can add parameter
159         // information to the route options.
160         if ($bundle_entity_type_id = $entity_type->getBundleEntityType()) {
161           $bundle_entity_type = $this->entityTypeManager->getDefinition($bundle_entity_type_id);
162
163           $route
164             // The title callback uses the value of the bundle parameter to
165             // fetch the respective bundle at runtime.
166             ->setDefault('bundle_parameter', $bundle_entity_type_id)
167             ->setRequirement('_entity_create_access', $entity_type_id . ':{' . $bundle_entity_type_id . '}');
168
169           // Entity types with serial IDs can specify this in their route
170           // requirements, improving the matching process.
171           if ($this->getEntityTypeIdKeyType($bundle_entity_type) === 'integer') {
172             $route->setRequirement($entity_type_id, '\d+');
173           }
174
175           $bundle_entity_parameter = ['type' => 'entity:' . $bundle_entity_type_id];
176           if ($bundle_entity_type instanceof ConfigEntityTypeInterface) {
177             // The add page might be displayed on an admin path. Even then, we
178             // need to load configuration overrides so that, for example, the
179             // bundle label gets translated correctly.
180             // @see \Drupal\Core\ParamConverter\AdminPathConfigEntityConverter
181             $bundle_entity_parameter['with_config_overrides'] = TRUE;
182           }
183           $route->setOption('parameters', [$bundle_entity_type_id => $bundle_entity_parameter]);
184         }
185         else {
186           // If the bundles are not entities, the bundle key is used as the
187           // route parameter name directly.
188           $route
189             ->setDefault('bundle_parameter', $bundle_key)
190             ->setRequirement('_entity_create_access', $entity_type_id . ':{' . $bundle_key . '}');
191         }
192       }
193       else {
194         $route
195           ->setDefault('_title_callback', EntityController::class . '::addTitle')
196           ->setRequirement('_entity_create_access', $entity_type_id);
197       }
198
199       return $route;
200     }
201   }
202
203   /**
204    * Gets the canonical route.
205    *
206    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
207    *   The entity type.
208    *
209    * @return \Symfony\Component\Routing\Route|null
210    *   The generated route, if available.
211    */
212   protected function getCanonicalRoute(EntityTypeInterface $entity_type) {
213     if ($entity_type->hasLinkTemplate('canonical') && $entity_type->hasViewBuilderClass()) {
214       $entity_type_id = $entity_type->id();
215       $route = new Route($entity_type->getLinkTemplate('canonical'));
216       $route
217         ->addDefaults([
218           '_entity_view' => "{$entity_type_id}.full",
219           '_title_callback' => '\Drupal\Core\Entity\Controller\EntityController::title',
220         ])
221         ->setRequirement('_entity_access', "{$entity_type_id}.view")
222         ->setOption('parameters', [
223           $entity_type_id => ['type' => 'entity:' . $entity_type_id],
224         ]);
225
226       // Entity types with serial IDs can specify this in their route
227       // requirements, improving the matching process.
228       if ($this->getEntityTypeIdKeyType($entity_type) === 'integer') {
229         $route->setRequirement($entity_type_id, '\d+');
230       }
231       return $route;
232     }
233   }
234
235   /**
236    * Gets the edit-form route.
237    *
238    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
239    *   The entity type.
240    *
241    * @return \Symfony\Component\Routing\Route|null
242    *   The generated route, if available.
243    */
244   protected function getEditFormRoute(EntityTypeInterface $entity_type) {
245     if ($entity_type->hasLinkTemplate('edit-form')) {
246       $entity_type_id = $entity_type->id();
247       $route = new Route($entity_type->getLinkTemplate('edit-form'));
248       // Use the edit form handler, if available, otherwise default.
249       $operation = 'default';
250       if ($entity_type->getFormClass('edit')) {
251         $operation = 'edit';
252       }
253       $route
254         ->setDefaults([
255           '_entity_form' => "{$entity_type_id}.{$operation}",
256           '_title_callback' => '\Drupal\Core\Entity\Controller\EntityController::editTitle'
257         ])
258         ->setRequirement('_entity_access', "{$entity_type_id}.update")
259         ->setOption('parameters', [
260           $entity_type_id => ['type' => 'entity:' . $entity_type_id],
261         ]);
262
263       // Entity types with serial IDs can specify this in their route
264       // requirements, improving the matching process.
265       if ($this->getEntityTypeIdKeyType($entity_type) === 'integer') {
266         $route->setRequirement($entity_type_id, '\d+');
267       }
268       return $route;
269     }
270   }
271
272   /**
273    * Gets the delete-form route.
274    *
275    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
276    *   The entity type.
277    *
278    * @return \Symfony\Component\Routing\Route|null
279    *   The generated route, if available.
280    */
281   protected function getDeleteFormRoute(EntityTypeInterface $entity_type) {
282     if ($entity_type->hasLinkTemplate('delete-form')) {
283       $entity_type_id = $entity_type->id();
284       $route = new Route($entity_type->getLinkTemplate('delete-form'));
285       $route
286         ->addDefaults([
287           '_entity_form' => "{$entity_type_id}.delete",
288           '_title_callback' => '\Drupal\Core\Entity\Controller\EntityController::deleteTitle',
289         ])
290         ->setRequirement('_entity_access', "{$entity_type_id}.delete")
291         ->setOption('parameters', [
292           $entity_type_id => ['type' => 'entity:' . $entity_type_id],
293         ]);
294
295       // Entity types with serial IDs can specify this in their route
296       // requirements, improving the matching process.
297       if ($this->getEntityTypeIdKeyType($entity_type) === 'integer') {
298         $route->setRequirement($entity_type_id, '\d+');
299       }
300       return $route;
301     }
302   }
303
304   /**
305    * Gets the collection route.
306    *
307    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
308    *   The entity type.
309    *
310    * @return \Symfony\Component\Routing\Route|null
311    *   The generated route, if available.
312    */
313   protected function getCollectionRoute(EntityTypeInterface $entity_type) {
314     // If the entity type does not provide an admin permission, there is no way
315     // to control access, so we cannot provide a route in a sensible way.
316     if ($entity_type->hasLinkTemplate('collection') && $entity_type->hasListBuilderClass() && ($admin_permission = $entity_type->getAdminPermission())) {
317       /** @var \Drupal\Core\StringTranslation\TranslatableMarkup $label */
318       $label = $entity_type->getCollectionLabel();
319
320       $route = new Route($entity_type->getLinkTemplate('collection'));
321       $route
322         ->addDefaults([
323           '_entity_list' => $entity_type->id(),
324           '_title' => $label->getUntranslatedString(),
325           '_title_arguments' => $label->getArguments(),
326           '_title_context' => $label->getOption('context'),
327         ])
328         ->setRequirement('_permission', $admin_permission);
329
330       return $route;
331     }
332   }
333
334   /**
335    * Gets the type of the ID key for a given entity type.
336    *
337    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
338    *   An entity type.
339    *
340    * @return string|null
341    *   The type of the ID key for a given entity type, or NULL if the entity
342    *   type does not support fields.
343    */
344   protected function getEntityTypeIdKeyType(EntityTypeInterface $entity_type) {
345     if (!$entity_type->entityClassImplements(FieldableEntityInterface::class)) {
346       return NULL;
347     }
348
349     $field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type->id());
350     return $field_storage_definitions[$entity_type->getKey('id')]->getType();
351   }
352
353 }