46cc730b9e5b02a1c8b8d4b83bdf163fda97b9df
[yaffs-website] / web / core / lib / Drupal / Core / Entity / Controller / EntityController.php
1 <?php
2
3 namespace Drupal\Core\Entity\Controller;
4
5 use Drupal\Core\Entity\EntityDescriptionInterface;
6 use Drupal\Core\Entity\EntityInterface;
7 use Drupal\Core\Entity\EntityRepositoryInterface;
8 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
9 use Drupal\Core\Entity\EntityTypeManagerInterface;
10 use Drupal\Core\Entity\EntityTypeInterface;
11 use Drupal\Core\Link;
12 use Drupal\Core\Render\RendererInterface;
13 use Drupal\Core\Routing\RouteMatchInterface;
14 use Drupal\Core\Routing\UrlGeneratorInterface;
15 use Drupal\Core\Routing\UrlGeneratorTrait;
16 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
17 use Drupal\Core\StringTranslation\StringTranslationTrait;
18 use Drupal\Core\StringTranslation\TranslationInterface;
19 use Symfony\Component\DependencyInjection\ContainerInterface;
20
21 /**
22  * Provides the add-page and title callbacks for entities.
23  *
24  * It provides:
25  * - The add-page callback.
26  * - An add title callback for entity types.
27  * - An add title callback for entity types with bundles.
28  * - A view title callback.
29  * - An edit title callback.
30  * - A delete title callback.
31  */
32 class EntityController implements ContainerInjectionInterface {
33
34   use StringTranslationTrait;
35   use UrlGeneratorTrait;
36
37   /**
38    * The entity manager.
39    *
40    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
41    */
42   protected $entityTypeManager;
43
44   /**
45    * The entity type bundle info.
46    *
47    * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
48    */
49   protected $entityTypeBundleInfo;
50
51   /**
52    * The entity repository.
53    *
54    * @var \Drupal\Core\Entity\EntityRepositoryInterface
55    */
56   protected $entityRepository;
57
58   /**
59    * The renderer.
60    *
61    * @var \Drupal\Core\Render\RendererInterface
62    */
63   protected $renderer;
64
65   /**
66    * Constructs a new EntityController.
67    *
68    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
69    *   The entity type manager.
70    * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
71    *   The entity type bundle info.
72    * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
73    *   The entity repository.
74    * @param \Drupal\Core\Render\RendererInterface $renderer
75    *   The renderer.
76    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
77    *   The string translation.
78    * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
79    *   The url generator.
80    */
81   public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityRepositoryInterface $entity_repository, RendererInterface $renderer, TranslationInterface $string_translation, UrlGeneratorInterface $url_generator) {
82     $this->entityTypeManager = $entity_type_manager;
83     $this->entityTypeBundleInfo = $entity_type_bundle_info;
84     $this->entityRepository = $entity_repository;
85     $this->renderer = $renderer;
86     $this->stringTranslation = $string_translation;
87     $this->urlGenerator = $url_generator;
88   }
89
90   /**
91    * {@inheritdoc}
92    */
93   public static function create(ContainerInterface $container) {
94     return new static(
95       $container->get('entity_type.manager'),
96       $container->get('entity_type.bundle.info'),
97       $container->get('entity.repository'),
98       $container->get('renderer'),
99       $container->get('string_translation'),
100       $container->get('url_generator')
101     );
102   }
103
104   /**
105    * Displays add links for the available bundles.
106    *
107    * Redirects to the add form if there's only one bundle available.
108    *
109    * @param string $entity_type_id
110    *   The entity type ID.
111    *
112    * @return \Symfony\Component\HttpFoundation\RedirectResponse|array
113    *   If there's only one available bundle, a redirect response.
114    *   Otherwise, a render array with the add links for each bundle.
115    */
116   public function addPage($entity_type_id) {
117     $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
118     $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
119     $bundle_key = $entity_type->getKey('bundle');
120     $bundle_entity_type_id = $entity_type->getBundleEntityType();
121     $build = [
122       '#theme' => 'entity_add_list',
123       '#bundles' => [],
124     ];
125     if ($bundle_entity_type_id) {
126       $bundle_argument = $bundle_entity_type_id;
127       $bundle_entity_type = $this->entityTypeManager->getDefinition($bundle_entity_type_id);
128       $bundle_entity_type_label = $bundle_entity_type->getLowercaseLabel();
129       $build['#cache']['tags'] = $bundle_entity_type->getListCacheTags();
130
131       // Build the message shown when there are no bundles.
132       $link_text = $this->t('Add a new @entity_type.', ['@entity_type' => $bundle_entity_type_label]);
133       $link_route_name = 'entity.' . $bundle_entity_type->id() . '.add_form';
134       $build['#add_bundle_message'] = $this->t('There is no @entity_type yet. @add_link', [
135         '@entity_type' => $bundle_entity_type_label,
136         '@add_link' => Link::createFromRoute($link_text, $link_route_name)->toString(),
137       ]);
138       // Filter out the bundles the user doesn't have access to.
139       $access_control_handler = $this->entityTypeManager->getAccessControlHandler($entity_type_id);
140       foreach ($bundles as $bundle_name => $bundle_info) {
141         $access = $access_control_handler->createAccess($bundle_name, NULL, [], TRUE);
142         if (!$access->isAllowed()) {
143           unset($bundles[$bundle_name]);
144         }
145         $this->renderer->addCacheableDependency($build, $access);
146       }
147       // Add descriptions from the bundle entities.
148       $bundles = $this->loadBundleDescriptions($bundles, $bundle_entity_type);
149     }
150     else {
151       $bundle_argument = $bundle_key;
152     }
153
154     $form_route_name = 'entity.' . $entity_type_id . '.add_form';
155     // Redirect if there's only one bundle available.
156     if (count($bundles) == 1) {
157       $bundle_names = array_keys($bundles);
158       $bundle_name = reset($bundle_names);
159       return $this->redirect($form_route_name, [$bundle_argument => $bundle_name]);
160     }
161     // Prepare the #bundles array for the template.
162     foreach ($bundles as $bundle_name => $bundle_info) {
163       $build['#bundles'][$bundle_name] = [
164         'label' => $bundle_info['label'],
165         'description' => isset($bundle_info['description']) ? $bundle_info['description'] : '',
166         'add_link' => Link::createFromRoute($bundle_info['label'], $form_route_name, [$bundle_argument => $bundle_name]),
167       ];
168     }
169
170     return $build;
171   }
172
173   /**
174    * Provides a generic add title callback for an entity type.
175    *
176    * @param string $entity_type_id
177    *   The entity type ID.
178    *
179    * @return string
180    *   The title for the entity add page.
181    */
182   public function addTitle($entity_type_id) {
183     $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
184     return $this->t('Add @entity-type', ['@entity-type' => $entity_type->getLowercaseLabel()]);
185   }
186
187   /**
188    * Provides a generic add title callback for entities with bundles.
189    *
190    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
191    *   The route match.
192    * @param string $entity_type_id
193    *   The entity type ID.
194    * @param string $bundle_parameter
195    *   The name of the route parameter that holds the bundle.
196    *
197    * @return string
198    *   The title for the entity add page, if the bundle was found.
199    */
200   public function addBundleTitle(RouteMatchInterface $route_match, $entity_type_id, $bundle_parameter) {
201     $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
202     // If the entity has bundle entities, the parameter might have been upcasted
203     // so fetch the raw parameter.
204     $bundle = $route_match->getRawParameter($bundle_parameter);
205     if ((count($bundles) > 1) && isset($bundles[$bundle])) {
206       return $this->t('Add @bundle', ['@bundle' => $bundles[$bundle]['label']]);
207     }
208     // If the entity supports bundles generally, but only has a single bundle,
209     // the bundle is probably something like 'Default' so that it preferable to
210     // use the entity type label.
211     else {
212       return $this->addTitle($entity_type_id);
213     }
214   }
215
216   /**
217    * Provides a generic title callback for a single entity.
218    *
219    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
220    *   The route match.
221    * @param \Drupal\Core\Entity\EntityInterface $_entity
222    *   (optional) An entity, passed in directly from the request attributes.
223    *
224    * @return string|null
225    *   The title for the entity view page, if an entity was found.
226    */
227   public function title(RouteMatchInterface $route_match, EntityInterface $_entity = NULL) {
228     if ($entity = $this->doGetEntity($route_match, $_entity)) {
229       return $entity->label();
230     }
231   }
232
233   /**
234    * Provides a generic edit title callback.
235    *
236    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
237    *   The route match.
238    * @param \Drupal\Core\Entity\EntityInterface $_entity
239    *   (optional) An entity, passed in directly from the request attributes.
240    *
241    * @return string|null
242    *   The title for the entity edit page, if an entity was found.
243    */
244   public function editTitle(RouteMatchInterface $route_match, EntityInterface $_entity = NULL) {
245     if ($entity = $this->doGetEntity($route_match, $_entity)) {
246       return $this->t('Edit %label', ['%label' => $entity->label()]);
247     }
248   }
249
250   /**
251    * Provides a generic delete title callback.
252    *
253    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
254    *   The route match.
255    * @param \Drupal\Core\Entity\EntityInterface $_entity
256    *   (optional) An entity, passed in directly from the request attributes, and
257    *   set in \Drupal\Core\Entity\Enhancer\EntityRouteEnhancer.
258    *
259    * @return string
260    *   The title for the entity delete page.
261    */
262   public function deleteTitle(RouteMatchInterface $route_match, EntityInterface $_entity = NULL) {
263     if ($entity = $this->doGetEntity($route_match, $_entity)) {
264       return $this->t('Delete %label', ['%label' => $entity->label()]);
265     }
266   }
267
268   /**
269    * Determines the entity.
270    *
271    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
272    *   The route match.
273    * @param \Drupal\Core\Entity\EntityInterface $_entity
274    *   (optional) The entity, set in
275    *   \Drupal\Core\Entity\Enhancer\EntityRouteEnhancer.
276    *
277    * @return \Drupal\Core\Entity\EntityInterface|null
278    *   The entity, if it is passed in directly or if the first parameter of the
279    *   active route is an entity; otherwise, NULL.
280    */
281   protected function doGetEntity(RouteMatchInterface $route_match, EntityInterface $_entity = NULL) {
282     if ($_entity) {
283       $entity = $_entity;
284     }
285     else {
286       // Let's look up in the route object for the name of upcasted values.
287       foreach ($route_match->getParameters() as $parameter) {
288         if ($parameter instanceof EntityInterface) {
289           $entity = $parameter;
290           break;
291         }
292       }
293     }
294     if (isset($entity)) {
295       return $this->entityRepository->getTranslationFromContext($entity);
296     }
297   }
298
299   /**
300    * Expands the bundle information with descriptions, if known.
301    *
302    * @param array $bundles
303    *   An array of bundle information.
304    * @param \Drupal\Core\Entity\EntityTypeInterface $bundle_entity_type
305    *   The bundle entity type definition.
306    *
307    * @return array
308    *   The expanded array of bundle information.
309    */
310   protected function loadBundleDescriptions(array $bundles, EntityTypeInterface $bundle_entity_type) {
311     if (!$bundle_entity_type->entityClassImplements(EntityDescriptionInterface::class)) {
312       return $bundles;
313     }
314     $bundle_names = array_keys($bundles);
315     $storage = $this->entityTypeManager->getStorage($bundle_entity_type->id());
316     /** @var \Drupal\Core\Entity\EntityDescriptionInterface[] $bundle_entities */
317     $bundle_entities = $storage->loadMultiple($bundle_names);
318     foreach ($bundles as $bundle_name => &$bundle_info) {
319       if (isset($bundle_entities[$bundle_name])) {
320         $bundle_info['description'] = $bundle_entities[$bundle_name]->getDescription();
321       }
322     }
323
324     return $bundles;
325   }
326
327 }