1<?php
2
3namespace Drupal\Core\Field;
4
5use Drupal\Core\Cache\CacheBackendInterface;
6use Drupal\Core\Entity\EntityFieldManagerInterface;
7use Drupal\Core\Entity\EntityTypeManagerInterface;
8use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
9
10/**
11 * Reacts to field definition CRUD on behalf of the Entity system.
12 */
13class FieldDefinitionListener implements FieldDefinitionListenerInterface {
14
15  /**
16   * The entity type manager.
17   *
18   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
19   */
20  protected $entityTypeManager;
21
22  /**
23   * The key-value factory.
24   *
25   * @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
26   */
27  protected $keyValueFactory;
28
29  /**
30   * Cache backend instance.
31   *
32   * @var \Drupal\Core\Cache\CacheBackendInterface
33   */
34  protected $cacheBackend;
35
36  /**
37   * The entity field manager.
38   *
39   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
40   */
41  protected $entityFieldManager;
42
43  /**
44   * Constructs a new FieldDefinitionListener.
45   *
46   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
47   *   The entity type manager.
48   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
49   *   The entity field manager.
50   * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
51   *   The key-value factory.
52   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
53   *   The cache backend.
54   */
55  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, KeyValueFactoryInterface $key_value_factory, CacheBackendInterface $cache_backend) {
56    $this->entityTypeManager = $entity_type_manager;
57    $this->entityFieldManager = $entity_field_manager;
58    $this->keyValueFactory = $key_value_factory;
59    $this->cacheBackend = $cache_backend;
60  }
61
62  /**
63   * {@inheritdoc}
64   */
65  public function onFieldDefinitionCreate(FieldDefinitionInterface $field_definition) {
66    $entity_type_id = $field_definition->getTargetEntityTypeId();
67    $bundle = $field_definition->getTargetBundle();
68    $field_name = $field_definition->getName();
69
70    // Notify the storage about the new field.
71    $this->entityTypeManager->getStorage($entity_type_id)->onFieldDefinitionCreate($field_definition);
72
73    // Update the bundle field map key value collection, add the new field.
74    $bundle_field_map = $this->keyValueFactory->get('entity.definitions.bundle_field_map')->get($entity_type_id);
75    if (!isset($bundle_field_map[$field_name])) {
76      // This field did not exist yet, initialize it with the type and empty
77      // bundle list.
78      $bundle_field_map[$field_name] = [
79        'type' => $field_definition->getType(),
80        'bundles' => [],
81      ];
82    }
83    $bundle_field_map[$field_name]['bundles'][$bundle] = $bundle;
84    $this->keyValueFactory->get('entity.definitions.bundle_field_map')->set($entity_type_id, $bundle_field_map);
85
86    // Delete the cache entry.
87    $this->cacheBackend->delete('entity_field_map');
88
89    // If the field map is initialized, update it as well, so that calls to it
90    // do not have to rebuild it again.
91    if ($field_map = $this->entityFieldManager->getFieldMap()) {
92      if (!isset($field_map[$entity_type_id][$field_name])) {
93        // This field did not exist yet, initialize it with the type and empty
94        // bundle list.
95        $field_map[$entity_type_id][$field_name] = [
96          'type' => $field_definition->getType(),
97          'bundles' => [],
98        ];
99      }
100      $field_map[$entity_type_id][$field_name]['bundles'][$bundle] = $bundle;
101      $this->entityFieldManager->setFieldMap($field_map);
102    }
103  }
104
105  /**
106   * {@inheritdoc}
107   */
108  public function onFieldDefinitionUpdate(FieldDefinitionInterface $field_definition, FieldDefinitionInterface $original) {
109    // Notify the storage about the updated field.
110    $this->entityTypeManager->getStorage($field_definition->getTargetEntityTypeId())->onFieldDefinitionUpdate($field_definition, $original);
111  }
112
113  /**
114   * {@inheritdoc}
115   */
116  public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition) {
117    $entity_type_id = $field_definition->getTargetEntityTypeId();
118    $bundle = $field_definition->getTargetBundle();
119    $field_name = $field_definition->getName();
120
121    // Notify the storage about the field deletion.
122    $this->entityTypeManager->getStorage($entity_type_id)->onFieldDefinitionDelete($field_definition);
123
124    // Unset the bundle from the bundle field map key value collection.
125    $bundle_field_map = $this->keyValueFactory->get('entity.definitions.bundle_field_map')->get($entity_type_id);
126    unset($bundle_field_map[$field_name]['bundles'][$bundle]);
127    if (empty($bundle_field_map[$field_name]['bundles'])) {
128      // If there are no bundles left, remove the field from the map.
129      unset($bundle_field_map[$field_name]);
130    }
131    $this->keyValueFactory->get('entity.definitions.bundle_field_map')->set($entity_type_id, $bundle_field_map);
132
133    // Delete the cache entry.
134    $this->cacheBackend->delete('entity_field_map');
135
136    // If the field map is initialized, update it as well, so that calls to it
137    // do not have to rebuild it again.
138    if ($field_map = $this->entityFieldManager->getFieldMap()) {
139      unset($field_map[$entity_type_id][$field_name]['bundles'][$bundle]);
140      if (empty($field_map[$entity_type_id][$field_name]['bundles'])) {
141        unset($field_map[$entity_type_id][$field_name]);
142      }
143      $this->entityFieldManager->setFieldMap($field_map);
144    }
145  }
146
147}
148