EntityTypeManager.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  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. $this->container->get('entity.memory_cache')->reset();
  133. }
  134. }
  135. /**
  136. * {@inheritdoc}
  137. */
  138. public function hasHandler($entity_type, $handler_type) {
  139. if ($definition = $this->getDefinition($entity_type, FALSE)) {
  140. return $definition->hasHandlerClass($handler_type);
  141. }
  142. return FALSE;
  143. }
  144. /**
  145. * {@inheritdoc}
  146. */
  147. public function getStorage($entity_type) {
  148. return $this->getHandler($entity_type, 'storage');
  149. }
  150. /**
  151. * {@inheritdoc}
  152. */
  153. public function getListBuilder($entity_type) {
  154. return $this->getHandler($entity_type, 'list_builder');
  155. }
  156. /**
  157. * {@inheritdoc}
  158. */
  159. public function getFormObject($entity_type, $operation) {
  160. if (!$class = $this->getDefinition($entity_type, TRUE)->getFormClass($operation)) {
  161. throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a "%s" form class.', $entity_type, $operation));
  162. }
  163. $form_object = $this->classResolver->getInstanceFromDefinition($class);
  164. return $form_object
  165. ->setStringTranslation($this->stringTranslation)
  166. ->setModuleHandler($this->moduleHandler)
  167. ->setEntityTypeManager($this)
  168. ->setOperation($operation)
  169. // The entity manager cannot be injected due to a circular dependency.
  170. // @todo Remove this set call in https://www.drupal.org/node/2603542.
  171. ->setEntityManager(\Drupal::entityManager());
  172. }
  173. /**
  174. * {@inheritdoc}
  175. */
  176. public function getRouteProviders($entity_type) {
  177. if (!isset($this->handlers['route_provider'][$entity_type])) {
  178. $route_provider_classes = $this->getDefinition($entity_type, TRUE)->getRouteProviderClasses();
  179. foreach ($route_provider_classes as $type => $class) {
  180. $this->handlers['route_provider'][$entity_type][$type] = $this->createHandlerInstance($class, $this->getDefinition($entity_type));
  181. }
  182. }
  183. return isset($this->handlers['route_provider'][$entity_type]) ? $this->handlers['route_provider'][$entity_type] : [];
  184. }
  185. /**
  186. * {@inheritdoc}
  187. */
  188. public function getViewBuilder($entity_type) {
  189. return $this->getHandler($entity_type, 'view_builder');
  190. }
  191. /**
  192. * {@inheritdoc}
  193. */
  194. public function getAccessControlHandler($entity_type) {
  195. return $this->getHandler($entity_type, 'access');
  196. }
  197. /**
  198. * {@inheritdoc}
  199. */
  200. public function getHandler($entity_type, $handler_type) {
  201. if (!isset($this->handlers[$handler_type][$entity_type])) {
  202. $definition = $this->getDefinition($entity_type);
  203. $class = $definition->getHandlerClass($handler_type);
  204. if (!$class) {
  205. throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a %s handler.', $entity_type, $handler_type));
  206. }
  207. $this->handlers[$handler_type][$entity_type] = $this->createHandlerInstance($class, $definition);
  208. }
  209. return $this->handlers[$handler_type][$entity_type];
  210. }
  211. /**
  212. * {@inheritdoc}
  213. */
  214. public function createHandlerInstance($class, EntityTypeInterface $definition = NULL) {
  215. if (is_subclass_of($class, 'Drupal\Core\Entity\EntityHandlerInterface')) {
  216. $handler = $class::createInstance($this->container, $definition);
  217. }
  218. else {
  219. $handler = new $class($definition);
  220. }
  221. if (method_exists($handler, 'setModuleHandler')) {
  222. $handler->setModuleHandler($this->moduleHandler);
  223. }
  224. if (method_exists($handler, 'setStringTranslation')) {
  225. $handler->setStringTranslation($this->stringTranslation);
  226. }
  227. return $handler;
  228. }
  229. }