Commit 47e00307 authored by catch's avatar catch

Issue #2332935 by plach, alexpott, dawehner: Allow code to respond to entity/field schema changes.

parent 22c82583
......@@ -287,13 +287,13 @@ services:
arguments: ['@container.namespaces', '@cache.discovery', '@module_handler']
module_handler:
class: Drupal\Core\Extension\ModuleHandler
arguments: ['%container.modules%', '@cache.bootstrap']
arguments: ['%container.modules%', '@kernel', '@cache.bootstrap']
theme_handler:
class: Drupal\Core\Extension\ThemeHandler
arguments: ['@config.factory', '@module_handler', '@state', '@info_parser', '@logger.channel.default', '@asset.css.collection_optimizer', '@config.installer', '@config.manager', '@router.builder_indicator']
entity.manager:
class: Drupal\Core\Entity\EntityManager
arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@language_manager', '@string_translation', '@class_resolver', '@typed_data_manager', '@entity.definitions.installed']
arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@language_manager', '@string_translation', '@class_resolver', '@typed_data_manager', '@entity.definitions.installed', '@event_dispatcher']
parent: container.trait
tags:
- { name: plugin_manager_cache_clear }
......
......@@ -52,7 +52,8 @@ public function getServiceProviders($origin);
/**
* Gets the current container.
*
* @return ContainerInterface A ContainerInterface instance
* @return \Symfony\Component\DependencyInjection\ContainerInterface
* A ContainerInterface instance.
*/
public function getContainer();
......
......@@ -7,8 +7,8 @@
namespace Drupal\Core\Entity;
use Drupal\Core\Entity\Schema\EntityStorageSchemaInterface;
use Drupal\Core\Entity\Schema\DynamicallyFieldableEntityStorageSchemaInterface;
use Drupal\Core\Entity\Schema\EntityStorageSchemaInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
......
......@@ -10,25 +10,28 @@
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Component\Utility\String;
use Drupal\Core\DependencyInjection\ClassResolverInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\DependencyInjection\ClassResolverInterface;
use Drupal\Core\Entity\Exception\AmbiguousEntityClassException;
use Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldStorageDefinitionEvent;
use Drupal\Core\Field\FieldStorageDefinitionEvents;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\TypedData\TranslatableInterface;
use Drupal\Core\TypedData\TypedDataManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Manages entity type plugin definitions.
......@@ -122,6 +125,13 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
*/
protected $installedDefinitions;
/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* Static cache of bundle information.
*
......@@ -183,8 +193,10 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
* The typed data manager.
* @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $installed_definitions
* The keyvalue collection for tracking installed definitions.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher.
*/
public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TranslationInterface $translation_manager, ClassResolverInterface $class_resolver, TypedDataManager $typed_data_manager, KeyValueStoreInterface $installed_definitions) {
public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TranslationInterface $translation_manager, ClassResolverInterface $class_resolver, TypedDataManager $typed_data_manager, KeyValueStoreInterface $installed_definitions, EventDispatcherInterface $event_dispatcher) {
parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface', 'Drupal\Core\Entity\Annotation\EntityType');
$this->setCacheBackend($cache, 'entity_type', array('entity_types'));
......@@ -195,6 +207,7 @@ public function __construct(\Traversable $namespaces, ModuleHandlerInterface $mo
$this->classResolver = $class_resolver;
$this->typedDataManager = $typed_data_manager;
$this->installedDefinitions = $installed_definitions;
$this->eventDispatcher = $event_dispatcher;
}
/**
......@@ -986,6 +999,8 @@ public function onEntityTypeCreate(EntityTypeInterface $entity_type) {
$storage->onEntityTypeCreate($entity_type);
}
$this->eventDispatcher->dispatch(EntityTypeEvents::CREATE, new EntityTypeEvent($entity_type));
$this->setLastInstalledDefinition($entity_type);
if ($entity_type->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
$this->setLastInstalledFieldStorageDefinitions($entity_type_id, $this->getFieldStorageDefinitions($entity_type_id));
......@@ -1005,6 +1020,8 @@ public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeI
$storage->onEntityTypeUpdate($entity_type, $original);
}
$this->eventDispatcher->dispatch(EntityTypeEvents::UPDATE, new EntityTypeEvent($entity_type, $original));
$this->setLastInstalledDefinition($entity_type);
}
......@@ -1021,6 +1038,8 @@ public function onEntityTypeDelete(EntityTypeInterface $entity_type) {
$storage->onEntityTypeDelete($entity_type);
}
$this->eventDispatcher->dispatch(EntityTypeEvents::DELETE, new EntityTypeEvent($entity_type));
$this->deleteLastInstalledDefinition($entity_type_id);
}
......@@ -1037,6 +1056,8 @@ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $
$storage->onFieldStorageDefinitionCreate($storage_definition);
}
$this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::CREATE, new FieldStorageDefinitionEvent($storage_definition));
$this->setLastInstalledFieldStorageDefinition($storage_definition);
$this->clearCachedFieldDefinitions();
}
......@@ -1054,6 +1075,8 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $
$storage->onFieldStorageDefinitionUpdate($storage_definition, $original);
}
$this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::UPDATE, new FieldStorageDefinitionEvent($storage_definition, $original));
$this->setLastInstalledFieldStorageDefinition($storage_definition);
$this->clearCachedFieldDefinitions();
}
......@@ -1071,6 +1094,8 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
$storage->onFieldStorageDefinitionDelete($storage_definition);
}
$this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::DELETE, new FieldStorageDefinitionEvent($storage_definition));
$this->deleteLastInstalledFieldStorageDefinition($storage_definition);
$this->clearCachedFieldDefinitions();
}
......
<?php
/**
* @file
* Contains \Drupal\Core\Entity\EntityTypeEvent.
*/
namespace Drupal\Core\Entity;
use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Defines a base class for all entity type events.
*/
class EntityTypeEvent extends GenericEvent {
/**
* The entity type.
*
* @var \Drupal\Core\Entity\EntityTypeInterface
*/
protected $entityType;
/**
* The original entity type.
*
* @var \Drupal\Core\Entity\EntityTypeInterface
*/
protected $original;
/**
* Constructs a new EntityTypeEvent.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The field storage definition.
* @param \Drupal\Core\Entity\EntityTypeInterface $original
* (optional) The original entity type. This should be passed only when
* updating the entity type.
*/
public function __construct(EntityTypeInterface $entity_type, EntityTypeInterface $original = NULL) {
$this->entityType = $entity_type;
$this->original = $original;
}
/**
* The entity type the event refers to.
*
* @return \Drupal\Core\Entity\EntityTypeInterface
*/
public function getEntityType() {
return $this->entityType;
}
/**
* The original entity type.
*
* @return \Drupal\Core\Entity\EntityTypeInterface
*/
public function getOriginal() {
return $this->original;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Entity\EntityTypeEventSubscriberTrait.
*/
namespace Drupal\Core\Entity;
/**
* Helper methods for EntityTypeListenerInterface.
*
* This allows a class implementing EntityTypeListenerInterface to subscribe and
* react to entity type events.
*
* @see \Symfony\Component\EventDispatcher\EventSubscriberInterface
* @see \Drupal\Core\Entity\EntityTypeListenerInterface
*/
trait EntityTypeEventSubscriberTrait {
/**
* Returns the subscribed events.
*
* @return array
* An array of subscribed event names.
*
* @see \Symfony\Component\EventDispatcher\EventSubscriberInterface::getSubscribedEvents()
*/
public static function getEntityTypeEvents() {
$event = array('onEntityTypeEvent', 100);
$events[EntityTypeEvents::CREATE][] = $event;
$events[EntityTypeEvents::UPDATE][] = $event;
$events[EntityTypeEvents::DELETE][] = $event;
return $events;
}
/**
* Listener method for any entity type definition event.
*
* @param \Drupal\Core\Entity\EntityTypeEvent $event
* The field storage definition event object.
* @param string $event_name
* The event name.
*/
public function onEntityTypeEvent(EntityTypeEvent $event, $event_name) {
switch ($event_name) {
case EntityTypeEvents::CREATE:
$this->onEntityTypeCreate($event->getEntityType());
break;
case EntityTypeEvents::UPDATE:
$this->onEntityTypeUpdate($event->getEntityType(), $event->getOriginal());
break;
case EntityTypeEvents::DELETE:
$this->onEntityTypeDelete($event->getEntityType());
break;
}
}
/**
* {@inheritdoc}
*/
abstract public function onEntityTypeCreate(EntityTypeInterface $entity_type);
/**
* {@inheritdoc}
*/
abstract public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original);
/**
* {@inheritdoc}
*/
abstract public function onEntityTypeDelete(EntityTypeInterface $entity_type);
}
<?php
/**
* @file
* Contains \Drupal\Core\Entity\EntityTypeEvents.
*/
namespace Drupal\Core\Entity;
/**
* Contains all events thrown while handling entity types.
*/
final class EntityTypeEvents {
/**
* Event name for entity type creation.
*
* @var string
*/
const CREATE = 'entity_type.definition.create';
/**
* Event name for entity type update.
*
* @var string
*/
const UPDATE = 'entity_type.definition.update';
/**
* Event name for entity type deletion.
*
* @var string
*/
const DELETE = 'entity_type.definition.delete';
}
......@@ -12,7 +12,7 @@
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\String;
use Drupal\Core\Cache\CacheBackendInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\DrupalKernelInterface;
/**
* Class that manages modules in a Drupal installation.
......@@ -64,6 +64,13 @@ class ModuleHandler implements ModuleHandlerInterface {
*/
protected $hookInfo;
/**
* The drupal kernel.
*
* @var \Drupal\Core\DrupalKernelInterface
*/
protected $kernel;
/**
* Cache backend for storing module hook implementation information.
*
......@@ -92,17 +99,20 @@ class ModuleHandler implements ModuleHandlerInterface {
* An associative array whose keys are the names of installed modules and
* whose values are Extension class parameters. This is normally the
* %container.modules% parameter being set up by DrupalKernel.
* @param \Drupal\Core\DrupalKernelInterface $kernel
* The drupal kernel.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend for storing module hook implementation information.
*
* @see \Drupal\Core\DrupalKernel
* @see \Drupal\Core\CoreServiceProvider
*/
public function __construct(array $module_list = array(), CacheBackendInterface $cache_backend) {
public function __construct(array $module_list = array(), DrupalKernelInterface $kernel, CacheBackendInterface $cache_backend) {
$this->moduleList = array();
foreach ($module_list as $name => $module) {
$this->moduleList[$name] = new Extension($module['type'], $module['pathname'], $module['filename']);
}
$this->kernel = $kernel;
$this->cacheBackend = $cache_backend;
}
......@@ -792,14 +802,7 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
drupal_static_reset('system_rebuild_module_data');
// Update the kernel to include it.
// This reboots the kernel to register the module's bundle and its
// services in the service container. The $module_filenames argument is
// taken over as %container.modules% parameter, which is passed to a
// fresh ModuleHandler instance upon first retrieval.
// @todo install_begin_request() creates a container without a kernel.
if ($kernel = \Drupal::service('kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
$kernel->updateModules($module_filenames, $module_filenames);
}
$this->updateKernel($module_filenames);
// Refresh the schema to include it.
drupal_get_schema(NULL, TRUE);
......@@ -822,10 +825,10 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
$version = max(max($versions), $version);
}
// Notify the entity manager that this module's entity types are new,
// so that it can notify all interested handlers. For example, a
// SQL-based storage handler can use this as an opportunity to create
// the necessary database tables.
// Notify interested components that this module's entity types are new.
// For example, a SQL-based storage handler can use this as an
// opportunity to create the necessary database tables.
// @todo Clean this up in https://www.drupal.org/node/2350111.
$entity_manager = \Drupal::entityManager();
foreach ($entity_manager->getDefinitions() as $entity_type) {
if ($entity_type->getProvider() == $module) {
......@@ -950,6 +953,7 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
// Clean up all entity bundles (including fields) of every entity type
// provided by the module that is being uninstalled.
// @todo Clean this up in https://www.drupal.org/node/2350111.
foreach ($entity_manager->getDefinitions() as $entity_type_id => $entity_type) {
if ($entity_type->getProvider() == $module) {
foreach (array_keys($entity_manager->getBundleInfo($entity_type_id)) as $bundle) {
......@@ -968,10 +972,10 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
// Remove all configuration belonging to the module.
\Drupal::service('config.manager')->uninstall('module', $module);
// Notify the entity manager that this module's entity types are being
// deleted, so that it can notify all interested handlers. For example,
// a SQL-based storage handler can use this as an opportunity to drop
// the corresponding database tables.
// Notify interested components that this module's entity types are being
// deleted. For example, a SQL-based storage handler can use this as an
// opportunity to drop the corresponding database tables.
// @todo Clean this up in https://www.drupal.org/node/2350111.
foreach ($entity_manager->getDefinitions() as $entity_type) {
if ($entity_type->getProvider() == $module) {
$entity_manager->onEntityTypeDelete($entity_type);
......@@ -1004,7 +1008,7 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
\Drupal::service('router.builder_indicator')->setRebuildNeeded();
// Update the kernel to exclude the uninstalled modules.
\Drupal::service('kernel')->updateModules($module_filenames, $module_filenames);
$this->updateKernel($module_filenames);
// Update the theme registry to remove the newly uninstalled module.
drupal_theme_rebuild();
......@@ -1084,4 +1088,23 @@ public function getName($module) {
$module_data = system_rebuild_module_data();
return $module_data[$module]->info['name'];
}
/**
* Updates the kernel module list.
*
* @param string $module_filenames
* The list of installed modules.
*/
protected function updateKernel($module_filenames) {
// This reboots the kernel to register the module's bundle and its services
// in the service container. The $module_filenames argument is taken over as
// %container.modules% parameter, which is passed to a fresh ModuleHandler
// instance upon first retrieval.
$this->kernel->updateModules($module_filenames, $module_filenames);
// After rebuilding the container we need to update the injected
// dependencies.
$container = $this->kernel->getContainer();
$this->cacheBackend = $container->get('cache.bootstrap');
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldStorageDefinitionEvent.
*/
namespace Drupal\Core\Field;
use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Defines a base class for all field storage definition events.
*/
class FieldStorageDefinitionEvent extends GenericEvent {
/**
* The field storage definition.
*
* @var \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
protected $fieldStorageDefinition;
/**
* The original field storage definition.
*
* @var \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
protected $original;
/**
* Constructs a new FieldStorageDefinitionEvent.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition
* The field storage definition.
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $original
* (optional) The original field storage definition. This should be passed
* only when updating the storage definition.
*/
public function __construct(FieldStorageDefinitionInterface $field_storage_definition, FieldStorageDefinitionInterface $original = NULL) {
$this->fieldStorageDefinition = $field_storage_definition;
$this->original = $original;
}
/**
* The field storage definition.
*
* @return \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
public function getFieldStorageDefinition() {
return $this->fieldStorageDefinition;
}
/**
* The original field storage definition.
*
* @return \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
public function getOriginal() {
return $this->original;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldStorageDefinitionEventSubscriberTrait.
*/
namespace Drupal\Core\Field;
/**
* Helper methods for FieldStorageDefinitionListenerInterface.
*
* This allows a class implementing FieldStorageDefinitionListenerInterface to
* subscribe and react to field storage definition events.
*
* @see \Symfony\Component\EventDispatcher\EventSubscriberInterface
* @see \Drupal\Core\Field\FieldStorageDefinitionListenerInterface
*/
trait FieldStorageDefinitionEventSubscriberTrait {
/**
* Returns the subscribed events.
*
* @return array
* An array of subscribed event names.
*
* @see \Symfony\Component\EventDispatcher\EventSubscriberInterface::getSubscribedEvents()
*/
public static function getFieldStorageDefinitionEvents() {
$event = array('onFieldStorageDefinitionEvent', 100);
$events[FieldStorageDefinitionEvents::CREATE][] = $event;
$events[FieldStorageDefinitionEvents::UPDATE][] = $event;
$events[FieldStorageDefinitionEvents::DELETE][] = $event;
return $events;
}
/**
* Listener method for any field storage definition event.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionEvent $event
* The field storage definition event object.
* @param string $event_name
* The event name.
*/
public function onFieldStorageDefinitionEvent(FieldStorageDefinitionEvent $event, $event_name) {
switch ($event_name) {
case FieldStorageDefinitionEvents::CREATE:
$this->onFieldStorageDefinitionCreate($event->getFieldStorageDefinition());
break;
case FieldStorageDefinitionEvents::UPDATE:
$this->onFieldStorageDefinitionUpdate($event->getFieldStorageDefinition(), $event->getOriginal());
break;
case FieldStorageDefinitionEvents::DELETE:
$this->onFieldStorageDefinitionDelete($event->getFieldStorageDefinition());
break;
}
}
/**
* {@inheritdoc}
*/
abstract public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition);
/**
* {@inheritdoc}
*/
abstract public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original);
/**
* {@inheritdoc}
*/
abstract public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition);
}
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldStorageDefinitionEvent.
*/
namespace Drupal\Core\Field;
/**
* Contains all events thrown while handling field storage definitions.
*/
final class FieldStorageDefinitionEvents {
/**
* Event name for field storage definition creation.
*
* @var string
*/
const CREATE = 'field_storage.definition.create';
/**
* Event name for field storage definition update.
*
* @var string
*/
const UPDATE = 'field_storage.definition.update';
/**
* Event name for field storage definition deletion.
*
* @var string
*/
const DELETE = 'field_storage.definition.delete';
}
......@@ -11,6 +11,7 @@
use Drupal\Component\Plugin\LazyPluginCollection;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Component\Plugin\ConfigurablePluginInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
/**
* Provides a default plugin collection for a plugin type.
......@@ -21,6 +22,7 @@
* self::$pluginKey.
*/
class DefaultLazyPluginCollection extends LazyPluginCollection {
use DependencySerializationTrait;
/**
* The manager used to instantiate the plugins.
......
......@@ -10,6 +10,7 @@
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Component\Plugin\LazyPluginCollection;
use Drupal\Component\Plugin\ConfigurablePluginInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
/**
* Provides a default plugin collection for a plugin type.
......@@ -21,6 +22,7 @@
* in self::initializePlugin().
*/
class DefaultSingleLazyPluginCollection extends LazyPluginCollection {
use DependencySerializationTrait;
/**
* The manager used to instantiate the plugins.
......
......@@ -8,8 +8,10 @@
namespace Drupal\system\Tests\Entity;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeEvents;
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldStorageDefinitionEvents;
use Drupal\entity_test\FieldStorageDefinition;
/**
......@@ -442,6 +444,39 @@ public function testEntityIndexCreateWithData() {
}
}
/**
* Tests entity type and field storage definition events.
*/
public function testDefinitionEvents() {
/** @var \Drupal\entity_test\EntityTestDefinitionSubscriber $event_subscriber */
$event_subscriber = $this->container->get('entity_test.definition.subscriber');
$event_subscriber->enableEventTracking();
// Test field storage definition events.
$storage_definition = current($this->entityManager->getFieldStorageDefinitions('entity_test_rev'));
$this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete was not dispatched yet.');
$this->entityManager->onFieldStorageDefinitionDelete($storage_definition);
$this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete event successfully dispatched.');
$this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create was not dispatched yet.');
$this->entityManager->onFieldStorageDefinitionCreate($storage_definition);
$this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create event successfully dispatched.');