abe96a5c264e8e64832dccbd95dc1cea8bee6c89
[yaffs-website] / web / core / lib / Drupal / Core / Entity / EntityTypeManager.php
1 <?php
2
3 namespace Drupal\Core\Entity;
4
5 use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
6 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
7 use Drupal\Core\Cache\CacheBackendInterface;
8 use Drupal\Core\DependencyInjection\ClassResolverInterface;
9 use Drupal\Core\Entity\Exception\InvalidLinkTemplateException;
10 use Drupal\Core\Extension\ModuleHandlerInterface;
11 use Drupal\Core\Plugin\DefaultPluginManager;
12 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
13 use Drupal\Core\StringTranslation\TranslationInterface;
14 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
15 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
16
17 /**
18  * Manages entity type plugin definitions.
19  *
20  * Each entity type definition array is set in the entity type's annotation and
21  * altered by hook_entity_type_alter().
22  *
23  * Do not use hook_entity_type_alter() hook to add information to entity types,
24  * unless one of the following is true:
25  * - You are filling in default values.
26  * - You need to dynamically add information only in certain circumstances.
27  * - Your hook needs to run after hook_entity_type_build() implementations.
28  * Use hook_entity_type_build() instead in all other cases.
29  *
30  * @see \Drupal\Core\Entity\Annotation\EntityType
31  * @see \Drupal\Core\Entity\EntityInterface
32  * @see \Drupal\Core\Entity\EntityTypeInterface
33  * @see hook_entity_type_alter()
34  * @see hook_entity_type_build()
35  */
36 class EntityTypeManager extends DefaultPluginManager implements EntityTypeManagerInterface, ContainerAwareInterface {
37
38   use ContainerAwareTrait;
39
40   /**
41    * Contains instantiated handlers keyed by handler type and entity type.
42    *
43    * @var array
44    */
45   protected $handlers = [];
46
47   /**
48    * The string translation service.
49    *
50    * @var \Drupal\Core\StringTranslation\TranslationInterface
51    */
52   protected $stringTranslation;
53
54   /**
55    * The class resolver.
56    *
57    * @var \Drupal\Core\DependencyInjection\ClassResolverInterface
58    */
59   protected $classResolver;
60
61   /**
62    * Constructs a new Entity plugin manager.
63    *
64    * @param \Traversable $namespaces
65    *   An object that implements \Traversable which contains the root paths
66    *   keyed by the corresponding namespace to look for plugin implementations,
67    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
68    *   The module handler.
69    * @param \Drupal\Core\Cache\CacheBackendInterface $cache
70    *   The cache backend to use.
71    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
72    *   The string translation.
73    * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
74    *   The class resolver.
75    */
76   public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, TranslationInterface $string_translation, ClassResolverInterface $class_resolver) {
77     parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface');
78
79     $this->setCacheBackend($cache, 'entity_type', ['entity_types']);
80     $this->alterInfo('entity_type');
81
82     $this->discovery = new AnnotatedClassDiscovery('Entity', $namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
83     $this->stringTranslation = $string_translation;
84     $this->classResolver = $class_resolver;
85   }
86
87   /**
88    * {@inheritdoc}
89    */
90   public function processDefinition(&$definition, $plugin_id) {
91     /** @var \Drupal\Core\Entity\EntityTypeInterface $definition */
92     parent::processDefinition($definition, $plugin_id);
93
94     // All link templates must have a leading slash.
95     foreach ((array) $definition->getLinkTemplates() as $link_relation_name => $link_template) {
96       if ($link_template[0] != '/') {
97         throw new InvalidLinkTemplateException("Link template '$link_relation_name' for entity type '$plugin_id' must start with a leading slash, the current link template is '$link_template'");
98       }
99     }
100   }
101
102   /**
103    * {@inheritdoc}
104    */
105   protected function findDefinitions() {
106     $definitions = $this->getDiscovery()->getDefinitions();
107
108     // Directly call the hook implementations to pass the definitions to them
109     // by reference, so new entity types can be added.
110     foreach ($this->moduleHandler->getImplementations('entity_type_build') as $module) {
111       $function = $module . '_' . 'entity_type_build';
112       $function($definitions);
113     }
114     foreach ($definitions as $plugin_id => $definition) {
115       $this->processDefinition($definition, $plugin_id);
116     }
117     $this->alterDefinitions($definitions);
118
119     return $definitions;
120   }
121
122   /**
123    * {@inheritdoc}
124    */
125   public function getDefinition($entity_type_id, $exception_on_invalid = TRUE) {
126     if (($entity_type = parent::getDefinition($entity_type_id, FALSE)) && class_exists($entity_type->getClass())) {
127       return $entity_type;
128     }
129     elseif (!$exception_on_invalid) {
130       return NULL;
131     }
132
133     throw new PluginNotFoundException($entity_type_id, sprintf('The "%s" entity type does not exist.', $entity_type_id));
134   }
135
136   /**
137    * {@inheritdoc}
138    */
139   public function clearCachedDefinitions() {
140     parent::clearCachedDefinitions();
141     $this->handlers = [];
142   }
143
144   /**
145    * {@inheritdoc}
146    */
147   public function useCaches($use_caches = FALSE) {
148     parent::useCaches($use_caches);
149     if (!$use_caches) {
150       $this->handlers = [];
151     }
152   }
153
154   /**
155    * {@inheritdoc}
156    */
157   public function hasHandler($entity_type, $handler_type) {
158     if ($definition = $this->getDefinition($entity_type, FALSE)) {
159       return $definition->hasHandlerClass($handler_type);
160     }
161
162     return FALSE;
163   }
164
165   /**
166    * {@inheritdoc}
167    */
168   public function getStorage($entity_type) {
169     return $this->getHandler($entity_type, 'storage');
170   }
171
172   /**
173    * {@inheritdoc}
174    */
175   public function getListBuilder($entity_type) {
176     return $this->getHandler($entity_type, 'list_builder');
177   }
178
179   /**
180    * {@inheritdoc}
181    */
182   public function getFormObject($entity_type, $operation) {
183     if (!$class = $this->getDefinition($entity_type, TRUE)->getFormClass($operation)) {
184       throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a "%s" form class.', $entity_type, $operation));
185     }
186
187     $form_object = $this->classResolver->getInstanceFromDefinition($class);
188
189     return $form_object
190       ->setStringTranslation($this->stringTranslation)
191       ->setModuleHandler($this->moduleHandler)
192       ->setEntityTypeManager($this)
193       ->setOperation($operation)
194       // The entity manager cannot be injected due to a circular dependency.
195       // @todo Remove this set call in https://www.drupal.org/node/2603542.
196       ->setEntityManager(\Drupal::entityManager());
197   }
198
199   /**
200    * {@inheritdoc}
201    */
202   public function getRouteProviders($entity_type) {
203     if (!isset($this->handlers['route_provider'][$entity_type])) {
204       $route_provider_classes = $this->getDefinition($entity_type, TRUE)->getRouteProviderClasses();
205
206       foreach ($route_provider_classes as $type => $class) {
207         $this->handlers['route_provider'][$entity_type][$type] = $this->createHandlerInstance($class, $this->getDefinition($entity_type));
208       }
209     }
210
211     return isset($this->handlers['route_provider'][$entity_type]) ? $this->handlers['route_provider'][$entity_type] : [];
212   }
213
214   /**
215    * {@inheritdoc}
216    */
217   public function getViewBuilder($entity_type) {
218     return $this->getHandler($entity_type, 'view_builder');
219   }
220
221   /**
222    * {@inheritdoc}
223    */
224   public function getAccessControlHandler($entity_type) {
225     return $this->getHandler($entity_type, 'access');
226   }
227
228   /**
229    * {@inheritdoc}
230    */
231   public function getHandler($entity_type, $handler_type) {
232     if (!isset($this->handlers[$handler_type][$entity_type])) {
233       $definition = $this->getDefinition($entity_type);
234       $class = $definition->getHandlerClass($handler_type);
235       if (!$class) {
236         throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a %s handler.', $entity_type, $handler_type));
237       }
238       $this->handlers[$handler_type][$entity_type] = $this->createHandlerInstance($class, $definition);
239     }
240
241     return $this->handlers[$handler_type][$entity_type];
242   }
243
244   /**
245    * {@inheritdoc}
246    */
247   public function createHandlerInstance($class, EntityTypeInterface $definition = NULL) {
248     if (is_subclass_of($class, 'Drupal\Core\Entity\EntityHandlerInterface')) {
249       $handler = $class::createInstance($this->container, $definition);
250     }
251     else {
252       $handler = new $class($definition);
253     }
254     if (method_exists($handler, 'setModuleHandler')) {
255       $handler->setModuleHandler($this->moduleHandler);
256     }
257     if (method_exists($handler, 'setStringTranslation')) {
258       $handler->setStringTranslation($this->stringTranslation);
259     }
260
261     return $handler;
262   }
263
264 }