3 namespace Drupal\Core\Field;
5 use Drupal\Core\Database\DatabaseExceptionWrapper;
6 use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
7 use Drupal\Core\Entity\EntityFieldManagerInterface;
8 use Drupal\Core\Entity\EntityTypeManagerInterface;
9 use Drupal\Core\Entity\FieldableEntityStorageInterface;
10 use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
11 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
14 * Reacts to field storage definition CRUD on behalf of the Entity system.
16 * @see \Drupal\Core\Field\FieldStorageDefinitionEvents
18 class FieldStorageDefinitionListener implements FieldStorageDefinitionListenerInterface {
21 * The entity type manager.
23 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
25 protected $entityTypeManager;
28 * The event dispatcher.
30 * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
32 protected $eventDispatcher;
35 * The entity definition manager.
37 * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
39 protected $entityLastInstalledSchemaRepository;
42 * The entity field manager.
44 * @var \Drupal\Core\Entity\EntityFieldManagerInterface
46 protected $entityFieldManager;
49 * The deleted fields repository.
51 * @var \Drupal\Core\Field\DeletedFieldsRepositoryInterface
53 protected $deletedFieldsRepository;
56 * Constructs a new FieldStorageDefinitionListener.
58 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
59 * The entity type manager.
60 * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
61 * The event dispatcher.
62 * @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository
63 * The entity last installed schema repository.
64 * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
65 * The entity field manager.
66 * @param \Drupal\Core\Field\DeletedFieldsRepositoryInterface $deleted_fields_repository
67 * The deleted fields repository.
69 public function __construct(EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository, EntityFieldManagerInterface $entity_field_manager, DeletedFieldsRepositoryInterface $deleted_fields_repository) {
70 $this->entityTypeManager = $entity_type_manager;
71 $this->eventDispatcher = $event_dispatcher;
72 $this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository;
73 $this->entityFieldManager = $entity_field_manager;
74 $this->deletedFieldsRepository = $deleted_fields_repository;
80 public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) {
81 $entity_type_id = $storage_definition->getTargetEntityTypeId();
83 // @todo Forward this to all interested handlers, not only storage, once
84 // iterating handlers is possible: https://www.drupal.org/node/2332857.
85 $storage = clone $this->entityTypeManager->getStorage($entity_type_id);
87 // Entity type definition updates can change the schema by adding or
88 // removing entity tables (for example when switching an entity type from
89 // non-revisionable to revisionable), so CRUD operations on a field storage
90 // definition need to use the last installed entity type schema.
91 if ($storage instanceof SqlContentEntityStorage
92 && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
93 $storage->setEntityType($last_installed_entity_type);
96 if ($storage instanceof FieldStorageDefinitionListenerInterface) {
97 $storage->onFieldStorageDefinitionCreate($storage_definition);
100 $this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::CREATE, new FieldStorageDefinitionEvent($storage_definition));
102 $this->entityLastInstalledSchemaRepository->setLastInstalledFieldStorageDefinition($storage_definition);
103 $this->entityFieldManager->clearCachedFieldDefinitions();
109 public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {
110 $entity_type_id = $storage_definition->getTargetEntityTypeId();
112 // @todo Forward this to all interested handlers, not only storage, once
113 // iterating handlers is possible: https://www.drupal.org/node/2332857.
114 $storage = clone $this->entityTypeManager->getStorage($entity_type_id);
116 // Entity type definition updates can change the schema by adding or
117 // removing entity tables (for example when switching an entity type from
118 // non-revisionable to revisionable), so CRUD operations on a field storage
119 // definition need to use the last installed entity type schema.
120 if ($storage instanceof SqlContentEntityStorage
121 && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
122 $storage->setEntityType($last_installed_entity_type);
125 if ($storage instanceof FieldStorageDefinitionListenerInterface) {
126 $storage->onFieldStorageDefinitionUpdate($storage_definition, $original);
129 $this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::UPDATE, new FieldStorageDefinitionEvent($storage_definition, $original));
131 $this->entityLastInstalledSchemaRepository->setLastInstalledFieldStorageDefinition($storage_definition);
132 $this->entityFieldManager->clearCachedFieldDefinitions();
138 public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) {
139 $entity_type_id = $storage_definition->getTargetEntityTypeId();
141 // @todo Forward this to all interested handlers, not only storage, once
142 // iterating handlers is possible: https://www.drupal.org/node/2332857.
143 $storage = clone $this->entityTypeManager->getStorage($entity_type_id);
145 // Entity type definition updates can change the schema by adding or
146 // removing entity tables (for example when switching an entity type from
147 // non-revisionable to revisionable), so CRUD operations on a field storage
148 // definition need to use the last installed entity type schema.
149 if ($storage instanceof SqlContentEntityStorage
150 && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
151 $storage->setEntityType($last_installed_entity_type);
154 // Keep the field definition in the deleted fields repository so we can use
155 // it later during field_purge_batch(), but only if the field has data.
157 if ($storage_definition instanceof BaseFieldDefinition && $storage instanceof FieldableEntityStorageInterface && $storage->countFieldData($storage_definition, TRUE)) {
158 $deleted_storage_definition = clone $storage_definition;
159 $deleted_storage_definition->setDeleted(TRUE);
160 $this->deletedFieldsRepository->addFieldDefinition($deleted_storage_definition);
161 $this->deletedFieldsRepository->addFieldStorageDefinition($deleted_storage_definition);
164 catch (DatabaseExceptionWrapper $e) {
165 // This may happen when changing field storage schema, since we are not
166 // able to use a table mapping matching the passed storage definition.
167 // @todo Revisit this once we are able to instantiate the table mapping
168 // properly. See https://www.drupal.org/node/2274017.
171 if ($storage instanceof FieldStorageDefinitionListenerInterface) {
172 $storage->onFieldStorageDefinitionDelete($storage_definition);
175 $this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::DELETE, new FieldStorageDefinitionEvent($storage_definition));
177 $this->entityLastInstalledSchemaRepository->deleteLastInstalledFieldStorageDefinition($storage_definition);
178 $this->entityFieldManager->clearCachedFieldDefinitions();