EntityFieldManager.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. <?php
  2. namespace Drupal\Core\Entity;
  3. use Drupal\Core\Cache\Cache;
  4. use Drupal\Core\Cache\CacheBackendInterface;
  5. use Drupal\Core\Cache\UseCacheBackendTrait;
  6. use Drupal\Core\Extension\ModuleHandlerInterface;
  7. use Drupal\Core\Field\BaseFieldDefinition;
  8. use Drupal\Core\Field\FieldDefinition;
  9. use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
  10. use Drupal\Core\Language\LanguageManagerInterface;
  11. use Drupal\Core\StringTranslation\StringTranslationTrait;
  12. use Drupal\Core\TypedData\TypedDataManagerInterface;
  13. /**
  14. * Manages the discovery of entity fields.
  15. *
  16. * This includes field definitions, base field definitions, and field storage
  17. * definitions.
  18. */
  19. class EntityFieldManager implements EntityFieldManagerInterface {
  20. use UseCacheBackendTrait;
  21. use StringTranslationTrait;
  22. /**
  23. * Extra fields by bundle.
  24. *
  25. * @var array
  26. */
  27. protected $extraFields = [];
  28. /**
  29. * Static cache of base field definitions.
  30. *
  31. * @var array
  32. */
  33. protected $baseFieldDefinitions;
  34. /**
  35. * Static cache of field definitions per bundle and entity type.
  36. *
  37. * @var array
  38. */
  39. protected $fieldDefinitions;
  40. /**
  41. * Static cache of field storage definitions per entity type.
  42. *
  43. * Elements of the array:
  44. * - $entity_type_id: \Drupal\Core\Field\BaseFieldDefinition[]
  45. *
  46. * @var array
  47. */
  48. protected $fieldStorageDefinitions;
  49. /**
  50. * Static cache of active field storage definitions per entity type.
  51. *
  52. * @var array
  53. */
  54. protected $activeFieldStorageDefinitions;
  55. /**
  56. * An array keyed by entity type. Each value is an array whose keys are
  57. * field names and whose value is an array with two entries:
  58. * - type: The field type.
  59. * - bundles: The bundles in which the field appears.
  60. *
  61. * @var array
  62. */
  63. protected $fieldMap = [];
  64. /**
  65. * An array keyed by field type. Each value is an array whose key are entity
  66. * types including arrays in the same form that $fieldMap.
  67. *
  68. * It helps access the mapping between types and fields by the field type.
  69. *
  70. * @var array
  71. */
  72. protected $fieldMapByFieldType = [];
  73. /**
  74. * The typed data manager.
  75. *
  76. * @var \Drupal\Core\TypedData\TypedDataManagerInterface
  77. */
  78. protected $typedDataManager;
  79. /**
  80. * The language manager.
  81. *
  82. * @var \Drupal\Core\Language\LanguageManagerInterface
  83. */
  84. protected $languageManager;
  85. /**
  86. * The key-value factory.
  87. *
  88. * @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
  89. */
  90. protected $keyValueFactory;
  91. /**
  92. * The module handler.
  93. *
  94. * @var \Drupal\Core\Extension\ModuleHandlerInterface
  95. */
  96. protected $moduleHandler;
  97. /**
  98. * The entity type manager.
  99. *
  100. * @var \Drupal\Core\Entity\EntityTypeManagerInterface
  101. */
  102. protected $entityTypeManager;
  103. /**
  104. * The entity type bundle info.
  105. *
  106. * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
  107. */
  108. protected $entityTypeBundleInfo;
  109. /**
  110. * The entity display repository.
  111. *
  112. * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
  113. */
  114. protected $entityDisplayRepository;
  115. /**
  116. * Constructs a new EntityFieldManager.
  117. *
  118. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
  119. * The entity type manager.
  120. * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
  121. * The entity type bundle info.
  122. * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
  123. * The entity display repository.
  124. * @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager
  125. * The typed data manager.
  126. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
  127. * The language manager.
  128. * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
  129. * The key-value factory.
  130. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
  131. * The module handler.
  132. * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
  133. * The cache backend.
  134. */
  135. public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityDisplayRepositoryInterface $entity_display_repository, TypedDataManagerInterface $typed_data_manager, LanguageManagerInterface $language_manager, KeyValueFactoryInterface $key_value_factory, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend) {
  136. $this->entityTypeManager = $entity_type_manager;
  137. $this->entityTypeBundleInfo = $entity_type_bundle_info;
  138. $this->entityDisplayRepository = $entity_display_repository;
  139. $this->typedDataManager = $typed_data_manager;
  140. $this->languageManager = $language_manager;
  141. $this->keyValueFactory = $key_value_factory;
  142. $this->moduleHandler = $module_handler;
  143. $this->cacheBackend = $cache_backend;
  144. }
  145. /**
  146. * {@inheritdoc}
  147. */
  148. public function getBaseFieldDefinitions($entity_type_id) {
  149. // Check the static cache.
  150. if (!isset($this->baseFieldDefinitions[$entity_type_id])) {
  151. // Not prepared, try to load from cache.
  152. $cid = 'entity_base_field_definitions:' . $entity_type_id . ':' . $this->languageManager->getCurrentLanguage()->getId();
  153. if ($cache = $this->cacheGet($cid)) {
  154. $this->baseFieldDefinitions[$entity_type_id] = $cache->data;
  155. }
  156. else {
  157. // Rebuild the definitions and put it into the cache.
  158. $this->baseFieldDefinitions[$entity_type_id] = $this->buildBaseFieldDefinitions($entity_type_id);
  159. $this->cacheSet($cid, $this->baseFieldDefinitions[$entity_type_id], Cache::PERMANENT, ['entity_types', 'entity_field_info']);
  160. }
  161. }
  162. return $this->baseFieldDefinitions[$entity_type_id];
  163. }
  164. /**
  165. * Builds base field definitions for an entity type.
  166. *
  167. * @param string $entity_type_id
  168. * The entity type ID. Only entity types that implement
  169. * \Drupal\Core\Entity\FieldableEntityInterface are supported.
  170. *
  171. * @return \Drupal\Core\Field\FieldDefinitionInterface[]
  172. * An array of field definitions, keyed by field name.
  173. *
  174. * @throws \LogicException
  175. * Thrown if a config entity type is given or if one of the entity keys is
  176. * flagged as translatable.
  177. */
  178. protected function buildBaseFieldDefinitions($entity_type_id) {
  179. /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
  180. $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
  181. $class = $entity_type->getClass();
  182. /** @var string[] $keys */
  183. $keys = array_filter($entity_type->getKeys());
  184. // Fail with an exception for non-fieldable entity types.
  185. if (!$entity_type->entityClassImplements(FieldableEntityInterface::class)) {
  186. throw new \LogicException("Getting the base fields is not supported for entity type {$entity_type->getLabel()}.");
  187. }
  188. // Retrieve base field definitions.
  189. /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface[] $base_field_definitions */
  190. $base_field_definitions = $class::baseFieldDefinitions($entity_type);
  191. // Make sure translatable entity types are correctly defined.
  192. if ($entity_type->isTranslatable()) {
  193. // The langcode field should always be translatable if the entity type is.
  194. if (isset($keys['langcode']) && isset($base_field_definitions[$keys['langcode']])) {
  195. $base_field_definitions[$keys['langcode']]->setTranslatable(TRUE);
  196. }
  197. // A default_langcode field should always be defined.
  198. if (!isset($base_field_definitions[$keys['default_langcode']])) {
  199. $base_field_definitions[$keys['default_langcode']] = BaseFieldDefinition::create('boolean')
  200. ->setLabel($this->t('Default translation'))
  201. ->setDescription($this->t('A flag indicating whether this is the default translation.'))
  202. ->setTranslatable(TRUE)
  203. ->setRevisionable(TRUE)
  204. ->setDefaultValue(TRUE);
  205. }
  206. }
  207. // Make sure that revisionable entity types are correctly defined.
  208. if ($entity_type->isRevisionable()) {
  209. // Disable the BC layer to prevent a recursion, this only needs the
  210. // revision_default key that is always set.
  211. $field_name = $entity_type->getRevisionMetadataKeys(FALSE)['revision_default'];
  212. $base_field_definitions[$field_name] = BaseFieldDefinition::create('boolean')
  213. ->setLabel($this->t('Default revision'))
  214. ->setDescription($this->t('A flag indicating whether this was a default revision when it was saved.'))
  215. ->setStorageRequired(TRUE)
  216. ->setInternal(TRUE)
  217. ->setTranslatable(FALSE)
  218. ->setRevisionable(TRUE);
  219. }
  220. // Make sure that revisionable and translatable entity types are correctly
  221. // defined.
  222. if ($entity_type->isRevisionable() && $entity_type->isTranslatable()) {
  223. // The 'revision_translation_affected' field should always be defined.
  224. // This field has been added unconditionally in Drupal 8.4.0 and it is
  225. // overriding any pre-existing definition on purpose so that any
  226. // differences are immediately available in the status report.
  227. $base_field_definitions[$keys['revision_translation_affected']] = BaseFieldDefinition::create('boolean')
  228. ->setLabel($this->t('Revision translation affected'))
  229. ->setDescription($this->t('Indicates if the last edit of a translation belongs to current revision.'))
  230. ->setReadOnly(TRUE)
  231. ->setRevisionable(TRUE)
  232. ->setTranslatable(TRUE);
  233. }
  234. // Assign base field definitions the entity type provider.
  235. $provider = $entity_type->getProvider();
  236. foreach ($base_field_definitions as $definition) {
  237. // @todo Remove this check once FieldDefinitionInterface exposes a proper
  238. // provider setter. See https://www.drupal.org/node/2225961.
  239. if ($definition instanceof BaseFieldDefinition) {
  240. $definition->setProvider($provider);
  241. }
  242. }
  243. // Retrieve base field definitions from modules.
  244. foreach ($this->moduleHandler->getImplementations('entity_base_field_info') as $module) {
  245. $module_definitions = $this->moduleHandler->invoke($module, 'entity_base_field_info', [$entity_type]);
  246. if (!empty($module_definitions)) {
  247. // Ensure the provider key actually matches the name of the provider
  248. // defining the field.
  249. foreach ($module_definitions as $field_name => $definition) {
  250. // @todo Remove this check once FieldDefinitionInterface exposes a
  251. // proper provider setter. See https://www.drupal.org/node/2225961.
  252. if ($definition instanceof BaseFieldDefinition && $definition->getProvider() == NULL) {
  253. $definition->setProvider($module);
  254. }
  255. $base_field_definitions[$field_name] = $definition;
  256. }
  257. }
  258. }
  259. // Automatically set the field name, target entity type and bundle
  260. // for non-configurable fields.
  261. foreach ($base_field_definitions as $field_name => $base_field_definition) {
  262. if ($base_field_definition instanceof BaseFieldDefinition) {
  263. $base_field_definition->setName($field_name);
  264. $base_field_definition->setTargetEntityTypeId($entity_type_id);
  265. $base_field_definition->setTargetBundle(NULL);
  266. }
  267. }
  268. // Invoke alter hook.
  269. $this->moduleHandler->alter('entity_base_field_info', $base_field_definitions, $entity_type);
  270. // Ensure defined entity keys are there and have proper revisionable and
  271. // translatable values.
  272. foreach (array_intersect_key($keys, array_flip(['id', 'revision', 'uuid', 'bundle'])) as $key => $field_name) {
  273. if (!isset($base_field_definitions[$field_name])) {
  274. throw new \LogicException("The $field_name field definition does not exist and it is used as $key entity key.");
  275. }
  276. if ($base_field_definitions[$field_name]->isRevisionable()) {
  277. throw new \LogicException("The {$base_field_definitions[$field_name]->getLabel()} field cannot be revisionable as it is used as $key entity key.");
  278. }
  279. if ($base_field_definitions[$field_name]->isTranslatable()) {
  280. throw new \LogicException("The {$base_field_definitions[$field_name]->getLabel()} field cannot be translatable as it is used as $key entity key.");
  281. }
  282. }
  283. // Make sure translatable entity types define the "langcode" field properly.
  284. if ($entity_type->isTranslatable() && (!isset($keys['langcode']) || !isset($base_field_definitions[$keys['langcode']]) || !$base_field_definitions[$keys['langcode']]->isTranslatable())) {
  285. throw new \LogicException("The {$entity_type->getLabel()} entity type cannot be translatable as it does not define a translatable \"langcode\" field.");
  286. }
  287. return $base_field_definitions;
  288. }
  289. /**
  290. * {@inheritdoc}
  291. */
  292. public function getFieldDefinitions($entity_type_id, $bundle) {
  293. $langcode = $this->languageManager->getCurrentLanguage()->getId();
  294. if (!isset($this->fieldDefinitions[$entity_type_id][$bundle][$langcode])) {
  295. $base_field_definitions = $this->getBaseFieldDefinitions($entity_type_id);
  296. // Not prepared, try to load from cache.
  297. $cid = 'entity_bundle_field_definitions:' . $entity_type_id . ':' . $bundle . ':' . $langcode;
  298. if ($cache = $this->cacheGet($cid)) {
  299. $bundle_field_definitions = $cache->data;
  300. }
  301. else {
  302. // Rebuild the definitions and put it into the cache.
  303. $bundle_field_definitions = $this->buildBundleFieldDefinitions($entity_type_id, $bundle, $base_field_definitions);
  304. $this->cacheSet($cid, $bundle_field_definitions, Cache::PERMANENT, ['entity_types', 'entity_field_info']);
  305. }
  306. // Field definitions consist of the bundle specific overrides and the
  307. // base fields, merge them together. Use array_replace() to replace base
  308. // fields with by bundle overrides and keep them in order, append
  309. // additional by bundle fields.
  310. $this->fieldDefinitions[$entity_type_id][$bundle][$langcode] = array_replace($base_field_definitions, $bundle_field_definitions);
  311. }
  312. return $this->fieldDefinitions[$entity_type_id][$bundle][$langcode];
  313. }
  314. /**
  315. * Builds field definitions for a specific bundle within an entity type.
  316. *
  317. * @param string $entity_type_id
  318. * The entity type ID. Only entity types that implement
  319. * \Drupal\Core\Entity\FieldableEntityInterface are supported.
  320. * @param string $bundle
  321. * The bundle.
  322. * @param \Drupal\Core\Field\FieldDefinitionInterface[] $base_field_definitions
  323. * The list of base field definitions.
  324. *
  325. * @return \Drupal\Core\Field\FieldDefinitionInterface[]
  326. * An array of bundle field definitions, keyed by field name. Does
  327. * not include base fields.
  328. */
  329. protected function buildBundleFieldDefinitions($entity_type_id, $bundle, array $base_field_definitions) {
  330. $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
  331. $class = $entity_type->getClass();
  332. // Allow the entity class to provide bundle fields and bundle-specific
  333. // overrides of base fields.
  334. $bundle_field_definitions = $class::bundleFieldDefinitions($entity_type, $bundle, $base_field_definitions);
  335. // Load base field overrides from configuration. These take precedence over
  336. // base field overrides returned above.
  337. $base_field_override_ids = array_map(function ($field_name) use ($entity_type_id, $bundle) {
  338. return $entity_type_id . '.' . $bundle . '.' . $field_name;
  339. }, array_keys($base_field_definitions));
  340. $base_field_overrides = $this->entityTypeManager->getStorage('base_field_override')->loadMultiple($base_field_override_ids);
  341. foreach ($base_field_overrides as $base_field_override) {
  342. /** @var \Drupal\Core\Field\Entity\BaseFieldOverride $base_field_override */
  343. $field_name = $base_field_override->getName();
  344. $bundle_field_definitions[$field_name] = $base_field_override;
  345. }
  346. $provider = $entity_type->getProvider();
  347. foreach ($bundle_field_definitions as $definition) {
  348. // @todo Remove this check once FieldDefinitionInterface exposes a proper
  349. // provider setter. See https://www.drupal.org/node/2225961.
  350. if ($definition instanceof BaseFieldDefinition) {
  351. $definition->setProvider($provider);
  352. }
  353. }
  354. // Retrieve base field definitions from modules.
  355. foreach ($this->moduleHandler->getImplementations('entity_bundle_field_info') as $module) {
  356. $module_definitions = $this->moduleHandler->invoke($module, 'entity_bundle_field_info', [$entity_type, $bundle, $base_field_definitions]);
  357. if (!empty($module_definitions)) {
  358. // Ensure the provider key actually matches the name of the provider
  359. // defining the field.
  360. foreach ($module_definitions as $field_name => $definition) {
  361. // @todo Remove this check once FieldDefinitionInterface exposes a
  362. // proper provider setter. See https://www.drupal.org/node/2225961.
  363. if ($definition instanceof BaseFieldDefinition) {
  364. $definition->setProvider($module);
  365. }
  366. $bundle_field_definitions[$field_name] = $definition;
  367. }
  368. }
  369. }
  370. // Automatically set the field name, target entity type and bundle
  371. // for non-configurable fields.
  372. foreach ($bundle_field_definitions as $field_name => $field_definition) {
  373. if ($field_definition instanceof BaseFieldDefinition) {
  374. $field_definition->setName($field_name);
  375. $field_definition->setTargetEntityTypeId($entity_type_id);
  376. }
  377. if ($field_definition instanceof BaseFieldDefinition || $field_definition instanceof FieldDefinition) {
  378. $field_definition->setTargetBundle($bundle);
  379. }
  380. }
  381. // Invoke 'per bundle' alter hook.
  382. $this->moduleHandler->alter('entity_bundle_field_info', $bundle_field_definitions, $entity_type, $bundle);
  383. return $bundle_field_definitions;
  384. }
  385. /**
  386. * {@inheritdoc}
  387. */
  388. public function getFieldStorageDefinitions($entity_type_id) {
  389. if (!isset($this->fieldStorageDefinitions[$entity_type_id])) {
  390. $this->fieldStorageDefinitions[$entity_type_id] = [];
  391. // Add all non-computed base fields.
  392. foreach ($this->getBaseFieldDefinitions($entity_type_id) as $field_name => $definition) {
  393. if (!$definition->isComputed()) {
  394. $this->fieldStorageDefinitions[$entity_type_id][$field_name] = $definition;
  395. }
  396. }
  397. // Not prepared, try to load from cache.
  398. $cid = 'entity_field_storage_definitions:' . $entity_type_id . ':' . $this->languageManager->getCurrentLanguage()->getId();
  399. if ($cache = $this->cacheGet($cid)) {
  400. $field_storage_definitions = $cache->data;
  401. }
  402. else {
  403. // Rebuild the definitions and put it into the cache.
  404. $field_storage_definitions = $this->buildFieldStorageDefinitions($entity_type_id);
  405. $this->cacheSet($cid, $field_storage_definitions, Cache::PERMANENT, ['entity_types', 'entity_field_info']);
  406. }
  407. $this->fieldStorageDefinitions[$entity_type_id] += $field_storage_definitions;
  408. }
  409. return $this->fieldStorageDefinitions[$entity_type_id];
  410. }
  411. /**
  412. * Gets the active field storage definitions for a content entity type.
  413. *
  414. * @param string $entity_type_id
  415. * The entity type ID. Only content entities are supported.
  416. *
  417. * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
  418. * An array of field storage definitions that are active in the current
  419. * request, keyed by field name.
  420. *
  421. * @internal
  422. */
  423. public function getActiveFieldStorageDefinitions($entity_type_id) {
  424. if (!isset($this->activeFieldStorageDefinitions[$entity_type_id])) {
  425. $this->activeFieldStorageDefinitions[$entity_type_id] = $this->keyValueFactory->get('entity.definitions.installed')->get($entity_type_id . '.field_storage_definitions', []);
  426. }
  427. return $this->activeFieldStorageDefinitions[$entity_type_id] ?: $this->getFieldStorageDefinitions($entity_type_id);
  428. }
  429. /**
  430. * {@inheritdoc}
  431. */
  432. public function setFieldMap(array $field_map) {
  433. $this->fieldMap = $field_map;
  434. return $this;
  435. }
  436. /**
  437. * {@inheritdoc}
  438. */
  439. public function getFieldMap() {
  440. if (!$this->fieldMap) {
  441. // Not prepared, try to load from cache.
  442. $cid = 'entity_field_map';
  443. if ($cache = $this->cacheGet($cid)) {
  444. $this->fieldMap = $cache->data;
  445. }
  446. else {
  447. // The field map is built in two steps. First, add all base fields, by
  448. // looping over all fieldable entity types. They always exist for all
  449. // bundles, and we do not expect to have so many different entity
  450. // types for this to become a bottleneck.
  451. foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
  452. if ($entity_type->entityClassImplements(FieldableEntityInterface::class)) {
  453. $bundles = array_keys($this->entityTypeBundleInfo->getBundleInfo($entity_type_id));
  454. foreach ($this->getBaseFieldDefinitions($entity_type_id) as $field_name => $base_field_definition) {
  455. $this->fieldMap[$entity_type_id][$field_name] = [
  456. 'type' => $base_field_definition->getType(),
  457. 'bundles' => array_combine($bundles, $bundles),
  458. ];
  459. }
  460. }
  461. }
  462. // In the second step, the per-bundle fields are added, based on the
  463. // persistent bundle field map stored in a key value collection. This
  464. // data is managed in the
  465. // FieldDefinitionListener::onFieldDefinitionCreate() and
  466. // FieldDefinitionListener::onFieldDefinitionDelete() methods.
  467. // Rebuilding this information in the same way as base fields would not
  468. // scale, as the time to query would grow exponentially with more fields
  469. // and bundles. A cache would be deleted during cache clears, which is
  470. // the only time it is needed, so a key value collection is used.
  471. $bundle_field_maps = $this->keyValueFactory->get('entity.definitions.bundle_field_map')->getAll();
  472. foreach ($bundle_field_maps as $entity_type_id => $bundle_field_map) {
  473. foreach ($bundle_field_map as $field_name => $map_entry) {
  474. if (!isset($this->fieldMap[$entity_type_id][$field_name])) {
  475. $this->fieldMap[$entity_type_id][$field_name] = $map_entry;
  476. }
  477. else {
  478. $this->fieldMap[$entity_type_id][$field_name]['bundles'] += $map_entry['bundles'];
  479. }
  480. }
  481. }
  482. $this->cacheSet($cid, $this->fieldMap, Cache::PERMANENT, ['entity_types', 'entity_field_info']);
  483. }
  484. }
  485. return $this->fieldMap;
  486. }
  487. /**
  488. * {@inheritdoc}
  489. */
  490. public function getFieldMapByFieldType($field_type) {
  491. if (!isset($this->fieldMapByFieldType[$field_type])) {
  492. $filtered_map = [];
  493. $map = $this->getFieldMap();
  494. foreach ($map as $entity_type => $fields) {
  495. foreach ($fields as $field_name => $field_info) {
  496. if ($field_info['type'] == $field_type) {
  497. $filtered_map[$entity_type][$field_name] = $field_info;
  498. }
  499. }
  500. }
  501. $this->fieldMapByFieldType[$field_type] = $filtered_map;
  502. }
  503. return $this->fieldMapByFieldType[$field_type];
  504. }
  505. /**
  506. * Builds field storage definitions for an entity type.
  507. *
  508. * @param string $entity_type_id
  509. * The entity type ID. Only entity types that implement
  510. * \Drupal\Core\Entity\FieldableEntityInterface are supported
  511. *
  512. * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
  513. * An array of field storage definitions, keyed by field name.
  514. */
  515. protected function buildFieldStorageDefinitions($entity_type_id) {
  516. $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
  517. $field_definitions = [];
  518. // Retrieve base field definitions from modules.
  519. foreach ($this->moduleHandler->getImplementations('entity_field_storage_info') as $module) {
  520. $module_definitions = $this->moduleHandler->invoke($module, 'entity_field_storage_info', [$entity_type]);
  521. if (!empty($module_definitions)) {
  522. // Ensure the provider key actually matches the name of the provider
  523. // defining the field.
  524. foreach ($module_definitions as $field_name => $definition) {
  525. // @todo Remove this check once FieldDefinitionInterface exposes a
  526. // proper provider setter. See https://www.drupal.org/node/2225961.
  527. if ($definition instanceof BaseFieldDefinition) {
  528. $definition->setProvider($module);
  529. }
  530. $field_definitions[$field_name] = $definition;
  531. }
  532. }
  533. }
  534. // Invoke alter hook.
  535. $this->moduleHandler->alter('entity_field_storage_info', $field_definitions, $entity_type);
  536. return $field_definitions;
  537. }
  538. /**
  539. * {@inheritdoc}
  540. */
  541. public function clearCachedFieldDefinitions() {
  542. $this->baseFieldDefinitions = [];
  543. $this->fieldDefinitions = [];
  544. $this->fieldStorageDefinitions = [];
  545. $this->activeFieldStorageDefinitions = [];
  546. $this->fieldMap = [];
  547. $this->fieldMapByFieldType = [];
  548. $this->entityDisplayRepository->clearDisplayModeInfo();
  549. $this->extraFields = [];
  550. Cache::invalidateTags(['entity_field_info']);
  551. // The typed data manager statically caches prototype objects with injected
  552. // definitions, clear those as well.
  553. $this->typedDataManager->clearCachedDefinitions();
  554. }
  555. /**
  556. * {@inheritdoc}
  557. */
  558. public function useCaches($use_caches = FALSE) {
  559. $this->useCaches = $use_caches;
  560. if (!$use_caches) {
  561. $this->fieldDefinitions = [];
  562. $this->baseFieldDefinitions = [];
  563. $this->fieldStorageDefinitions = [];
  564. $this->activeFieldStorageDefinitions = [];
  565. }
  566. }
  567. /**
  568. * {@inheritdoc}
  569. */
  570. public function getExtraFields($entity_type_id, $bundle) {
  571. // Read from the "static" cache.
  572. if (isset($this->extraFields[$entity_type_id][$bundle])) {
  573. return $this->extraFields[$entity_type_id][$bundle];
  574. }
  575. // Read from the persistent cache. Since hook_entity_extra_field_info() and
  576. // hook_entity_extra_field_info_alter() might contain t() calls, we cache
  577. // per language.
  578. $cache_id = 'entity_bundle_extra_fields:' . $entity_type_id . ':' . $bundle . ':' . $this->languageManager->getCurrentLanguage()->getId();
  579. $cached = $this->cacheGet($cache_id);
  580. if ($cached) {
  581. $this->extraFields[$entity_type_id][$bundle] = $cached->data;
  582. return $this->extraFields[$entity_type_id][$bundle];
  583. }
  584. $extra = $this->moduleHandler->invokeAll('entity_extra_field_info');
  585. $this->moduleHandler->alter('entity_extra_field_info', $extra);
  586. $info = isset($extra[$entity_type_id][$bundle]) ? $extra[$entity_type_id][$bundle] : [];
  587. $info += [
  588. 'form' => [],
  589. 'display' => [],
  590. ];
  591. // Store in the 'static' and persistent caches.
  592. $this->extraFields[$entity_type_id][$bundle] = $info;
  593. $this->cacheSet($cache_id, $info, Cache::PERMANENT, [
  594. 'entity_field_info',
  595. ]);
  596. return $this->extraFields[$entity_type_id][$bundle];
  597. }
  598. }