EntityTypeManager.php 8.6 KB

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