diff --git a/core/lib/Drupal/Core/Entity/EntityEvent.php b/core/lib/Drupal/Core/Entity/EntityEvent.php new file mode 100644 index 0000000..f504666 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/EntityEvent.php @@ -0,0 +1,42 @@ +entity = $entity; + } + + /** + * Returns the entity wrapped by this event. + * + * @return \Drupal\Core\Entity\EntityInterface + * The entity object. + */ + public function getEntity() { + return $this->entity; + } + +} diff --git a/core/lib/Drupal/Core/Entity/EntityEvents.php b/core/lib/Drupal/Core/Entity/EntityEvents.php new file mode 100644 index 0000000..3c62b17 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/EntityEvents.php @@ -0,0 +1,105 @@ + self::CREATE, + 'presave' => self::PRESAVE, + 'insert' => self::INSERT, + 'update' => self::UPDATE, + 'predelete' => self::PREDELETE, + 'delete' => self::DELETE, + ]; + + /** + * Name of the event fired when creating an entity. + * + * This hook runs after a new entity object has just been instantiated. + * + * @see hook_entity_create() + * + * @Event + * + * @var string + */ + const CREATE = 'entity.create'; + + /** + * Name of the event fired before an entity is created or updated. + * + * You can get the original entity object from $entity->original when it is an + * update of the entity. + * + * @see hook_entity_presave() + * + * @Event + * + * @var string + */ + const PRESAVE = 'entity.presave'; + + /** + * Name of the event fired after creating an entity. + * + * This event fires once the entity has been stored. Note that changes to the + * entity made by subscribers to the event will not be saved. + * + * @see hook_entity_insert() + * + * @Event + * + * @var string + */ + const INSERT = 'entity.insert'; + + /** + * Name of the event fired after updating an existing entity. + * + * This event fires once the entity has been stored. Note that changes to the + * entity made by subscribers to the event will not be saved. Get the original + * entity object from $entity->original. + * + * @see hook_entity_update() + * + * @Event + * + * @var string + */ + const UPDATE = 'entity.update'; + + /** + * Name of the event fired before deleting an entity. + * + * @see hook_entity_predelete() + * + * @Event + * + * @var string + */ + const PREDELETE = 'entity.predelete'; + + /** + * Name of the event fired after deleting an entity. + * + * @see hook_entity_delete() + * + * @Event + * + * @var string + */ + const DELETE = 'entity.delete'; + +} diff --git a/core/lib/Drupal/Core/Entity/EntityStorageBase.php b/core/lib/Drupal/Core/Entity/EntityStorageBase.php index f583121..2173485 100644 --- a/core/lib/Drupal/Core/Entity/EntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php @@ -167,6 +167,11 @@ protected function invokeHook($hook, EntityInterface $entity) { $this->moduleHandler()->invokeAll($this->entityTypeId . '_' . $hook, array($entity)); // Invoke the respective entity-level hook. $this->moduleHandler()->invokeAll('entity_' . $hook, array($entity)); + + $hook_to_event_map = EntityEvents::HOOK_TO_EVENT_MAP; + if (isset($hook_to_event_map[$hook])) { + \Drupal::service('event_dispatcher')->dispatch($hook_to_event_map[$hook], new EntityEvent($entity)); + } } /** diff --git a/core/modules/system/tests/modules/entity_test_event/entity_test_event.info.yml b/core/modules/system/tests/modules/entity_test_event/entity_test_event.info.yml new file mode 100644 index 0000000..5089f4b --- /dev/null +++ b/core/modules/system/tests/modules/entity_test_event/entity_test_event.info.yml @@ -0,0 +1,4 @@ +name: entity_test_event +type: module +package: Testing +core: 8.x diff --git a/core/modules/system/tests/modules/entity_test_event/entity_test_event.services.yml b/core/modules/system/tests/modules/entity_test_event/entity_test_event.services.yml new file mode 100644 index 0000000..e20270c --- /dev/null +++ b/core/modules/system/tests/modules/entity_test_event/entity_test_event.services.yml @@ -0,0 +1,5 @@ +services: + entity_test_event.event_subscriber: + class: \Drupal\entity_test_event\EventSubscriber\TestEventSubscriber + tags: + - { name: event_subscriber } diff --git a/core/modules/system/tests/modules/entity_test_event/src/EventSubscriber/TestEventSubscriber.php b/core/modules/system/tests/modules/entity_test_event/src/EventSubscriber/TestEventSubscriber.php new file mode 100644 index 0000000..8566ba0 --- /dev/null +++ b/core/modules/system/tests/modules/entity_test_event/src/EventSubscriber/TestEventSubscriber.php @@ -0,0 +1,44 @@ +getEntity()->name->value === 'hei') { + $event->getEntity()->name->value .= ' ho'; + } + } + + public function onUpdate(EntityEvent $event) { + if ($event->getEntity()->name->value === 'hei') { + $event->getEntity()->name->value .= ' ho'; + } + } + + public function onDelete(EntityEvent $event) { + if ($event->getEntity()->name->value === 'hei ho') { + EntityTest::create([ + 'name' => 'hei_ho' + ])->save(); + $event->getEntity()->name->value .= ' ho'; + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[EntityEvents::INSERT] = 'onInsert'; + $events[EntityEvents::UPDATE] = 'onUpdate'; + $events[EntityEvents::DELETE] = 'onDelete'; + return $events; + } + + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityEventsTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityEventsTest.php new file mode 100644 index 0000000..99faba98 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityEventsTest.php @@ -0,0 +1,64 @@ +installEntitySchema('entity_test'); + } + + public function testEventsInsert() { + $entity = EntityTest::create([ + 'name' => 'hei', + ]); + $entity->save(); + $this->assertEquals('hei ho', $entity->name->value); + } + + public function testEventsUpdate() { + $entity = EntityTest::create([ + 'name' => 'meh', + ]); + $entity->save(); + $this->assertEquals('meh', $entity->name->value); + + $entity->name->value = 'hei'; + $entity->save(); + $this->assertEquals('hei ho', $entity->name->value); + } + + public function testEventsDelete() { + $entities = \Drupal::entityTypeManager()->getStorage('entity_test') + ->loadByProperties(['name' => 'hei_ho']); + $this->assertCount(0, $entities); + + $entity = EntityTest::create([ + 'name' => 'hei', + ]); + $entity->save(); + $entity->delete(); + + // Note the delete event creates another entity. + $entities = \Drupal::entityTypeManager()->getStorage('entity_test') + ->loadByProperties(['name' => 'hei_ho']); + $this->assertCount(1, $entities); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php index 4fbdba3..ef4a51c 100644 --- a/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php @@ -8,6 +8,7 @@ use Drupal\Core\Language\Language; use Drupal\Tests\UnitTestCase; use Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * @coversDefaultClass \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage @@ -119,12 +120,14 @@ protected function setUpKeyValueEntityStorage($uuid_key = 'uuid') { $this->languageManager->expects($this->any()) ->method('getCurrentLanguage') ->will($this->returnValue($language)); + $event_dispatcher = $this->prophesize(EventDispatcherInterface::class); $this->entityStorage = new KeyValueEntityStorage($this->entityType, $this->keyValueStore, $this->uuidService, $this->languageManager); $this->entityStorage->setModuleHandler($this->moduleHandler); $container = new ContainerBuilder(); $container->set('entity.manager', $this->entityManager); + $container->set('event_dispatcher', $event_dispatcher->reveal()); $container->set('language_manager', $this->languageManager); $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator); \Drupal::setContainer($container); diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php index 56b4b00..064f61e 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php @@ -14,6 +14,7 @@ use Drupal\Core\Language\Language; use Drupal\Tests\UnitTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * @coversDefaultClass \Drupal\Core\Entity\Sql\SqlContentEntityStorage @@ -980,10 +981,12 @@ public function testCreate() { $language_manager->expects($this->any()) ->method('getCurrentLanguage') ->will($this->returnValue($language)); + $event_dispatcher = $this->prophesize(EventDispatcherInterface::class); $this->container->set('language_manager', $language_manager); $this->container->set('entity.manager', $this->entityManager); $this->container->set('module_handler', $this->moduleHandler); + $this->container->set('event_dispatcher', $event_dispatcher->reveal()); $entity = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase') ->disableOriginalConstructor()