diff --git a/doc/4-events.md b/doc/4-events.md index 231494d..27eba90 100644 --- a/doc/4-events.md +++ b/doc/4-events.md @@ -8,3 +8,29 @@ You can listen to the following events: **ElementInvalidationEvent**: Triggered before a Pimcore element is invalidated; allows canceling the invalidation process or performing custom actions. This allows you to add additional tags, cancel the tagging/invalidation process, or implement custom logic. + +### ElementInvalidationEvent: distinguishing update from delete + +`ElementInvalidationEvent` exposes a `type` property of type `InvalidationType`, which tells you whether the invalidation was triggered by a save or a delete: + +```php +use Neusta\Pimcore\HttpCacheBundle\Element\ElementInvalidationEvent; +use Neusta\Pimcore\HttpCacheBundle\Element\InvalidationType; +use Symfony\Component\EventDispatcher\Attribute\AsEventListener; + +#[AsEventListener] +final class MyInvalidationListener +{ + public function __invoke(ElementInvalidationEvent $event): void + { + if (InvalidationType::Delete === $event->type) { + // React differently on delete vs update + } + } +} +``` + +| `InvalidationType` | Triggered by | +|---|---| +| `InvalidationType::Update` | Element saved (`POST_UPDATE`) | +| `InvalidationType::Delete` | Element deleted (`PRE_DELETE`) | diff --git a/src/Element/ElementInvalidationEvent.php b/src/Element/ElementInvalidationEvent.php index 1e0fcaf..2f0d55f 100644 --- a/src/Element/ElementInvalidationEvent.php +++ b/src/Element/ElementInvalidationEvent.php @@ -12,15 +12,17 @@ final class ElementInvalidationEvent extends Event public bool $cancel = false; private function __construct( + public readonly InvalidationType $type, public readonly ElementInterface $element, public readonly ElementType $elementType, private CacheTags $cacheTags, ) { } - public static function fromElement(ElementInterface $element): self + public static function fromElement(InvalidationType $type, ElementInterface $element): self { return new self( + $type, $element, ElementType::fromElement($element), CacheTags::fromElement($element), diff --git a/src/Element/InvalidateElementListener.php b/src/Element/InvalidateElementListener.php index d8a2d8f..bc52507 100644 --- a/src/Element/InvalidateElementListener.php +++ b/src/Element/InvalidateElementListener.php @@ -21,17 +21,17 @@ public function onUpdate(ElementEventInterface $event): void return; } - $this->invalidateElement($event->getElement()); + $this->invalidateElement(InvalidationType::Update, $event->getElement()); } public function onDelete(ElementEventInterface $event): void { - $this->invalidateElement($event->getElement()); + $this->invalidateElement(InvalidationType::Delete, $event->getElement()); } - private function invalidateElement(ElementInterface $element): void + private function invalidateElement(InvalidationType $type, ElementInterface $element): void { - $invalidationEvent = $this->dispatcher->dispatch(ElementInvalidationEvent::fromElement($element)); + $invalidationEvent = $this->dispatcher->dispatch(ElementInvalidationEvent::fromElement($type, $element)); \assert($invalidationEvent instanceof ElementInvalidationEvent); if ($invalidationEvent->cancel) { diff --git a/src/Element/InvalidationType.php b/src/Element/InvalidationType.php new file mode 100644 index 0000000..6c8505c --- /dev/null +++ b/src/Element/InvalidationType.php @@ -0,0 +1,9 @@ + */ private ObjectProphecy $innerTagger; @@ -22,7 +22,7 @@ final class TraceableResponseTaggerTest extends TestCase protected function setUp(): void { $this->innerTagger = $this->prophesize(ResponseTagger::class); - $this->collectTagsResponseTagger = new TraceableResponseTagger($this->innerTagger->reveal()); + $this->traceableResponseTagger = new TraceableResponseTagger($this->innerTagger->reveal()); } /** @@ -30,7 +30,7 @@ protected function setUp(): void */ public function tag_should_collect_tags(): void { - $this->collectTagsResponseTagger->tag( + $this->traceableResponseTagger->tag( new CacheTags( CacheTag::fromString('tag1'), CacheTag::fromString('tag2'), @@ -38,7 +38,7 @@ public function tag_should_collect_tags(): void self::assertSame( 'tag1,tag2', - $this->collectTagsResponseTagger->recordedTags->toString(), + $this->traceableResponseTagger->recordedTags->toString(), ); } @@ -52,7 +52,7 @@ public function tag_should_forward_tags_to_inner_tagger(): void CacheTag::fromString('tag2'), ); - $this->collectTagsResponseTagger->tag($tags); + $this->traceableResponseTagger->tag($tags); $this->innerTagger->tag($tags)->shouldHaveBeenCalledOnce(); } @@ -62,16 +62,16 @@ public function tag_should_forward_tags_to_inner_tagger(): void */ public function reset_should_reset_collected_tags(): void { - $this->collectTagsResponseTagger->tag( + $this->traceableResponseTagger->tag( new CacheTags( CacheTag::fromString('tag1'), CacheTag::fromString('tag2'), )); - $this->collectTagsResponseTagger->reset(); + $this->traceableResponseTagger->reset(); self::assertTrue( - $this->collectTagsResponseTagger->recordedTags->isEmpty(), + $this->traceableResponseTagger->recordedTags->isEmpty(), ); } } diff --git a/tests/Unit/Element/InvalidateElementListenerTest.php b/tests/Unit/Element/InvalidateElementListenerTest.php index 21d541b..1167e08 100644 --- a/tests/Unit/Element/InvalidateElementListenerTest.php +++ b/tests/Unit/Element/InvalidateElementListenerTest.php @@ -7,6 +7,7 @@ use Neusta\Pimcore\HttpCacheBundle\Cache\CacheTags; use Neusta\Pimcore\HttpCacheBundle\Element\ElementInvalidationEvent; use Neusta\Pimcore\HttpCacheBundle\Element\InvalidateElementListener; +use Neusta\Pimcore\HttpCacheBundle\Element\InvalidationType; use PHPUnit\Framework\TestCase; use Pimcore\Event\Model\AssetEvent; use Pimcore\Event\Model\DataObjectEvent; @@ -113,7 +114,7 @@ public function onUpdate_should_invalidate_elements(ElementEventInterface $event public function onUpdate_does_not_invalidate_when_event_was_canceled(ElementEventInterface $event): void { $element = $event->getElement(); - $invalidationEvent = ElementInvalidationEvent::fromElement($element); + $invalidationEvent = ElementInvalidationEvent::fromElement(InvalidationType::Update, $element); $invalidationEvent->cancel = true; $this->eventDispatcher->dispatch(Argument::type(ElementInvalidationEvent::class)) @@ -135,7 +136,7 @@ public function onUpdate_should_invalidate_additional_tags_when_requested(Elemen $element = $event->getElement(); $additionalTag = CacheTag::fromString('tag1'); $additionalTags = CacheTags::fromStrings(['tag2', 'tag3']); - $invalidationEvent = ElementInvalidationEvent::fromElement($element); + $invalidationEvent = ElementInvalidationEvent::fromElement(InvalidationType::Update, $element); $invalidationEvent->addTag($additionalTag); $invalidationEvent->addTags($additionalTags); $expected = CacheTags::fromElement($element)->with($additionalTag, $additionalTags); @@ -185,7 +186,7 @@ public function onDelete_should_invalidate_elements(ElementEventInterface $event public function onDelete_does_not_invalidate_when_event_was_canceled(ElementEventInterface $event): void { $element = $event->getElement(); - $invalidationEvent = ElementInvalidationEvent::fromElement($element); + $invalidationEvent = ElementInvalidationEvent::fromElement(InvalidationType::Delete, $element); $invalidationEvent->cancel = true; $this->eventDispatcher->dispatch(Argument::type(ElementInvalidationEvent::class)) @@ -207,7 +208,7 @@ public function onDelete_should_invalidate_additional_tags_when_requested(Elemen $element = $event->getElement(); $additionalTag = CacheTag::fromString('tag1'); $additionalTags = CacheTags::fromStrings(['tag2', 'tag3']); - $invalidationEvent = ElementInvalidationEvent::fromElement($element); + $invalidationEvent = ElementInvalidationEvent::fromElement(InvalidationType::Delete, $element); $invalidationEvent->addTag($additionalTag); $invalidationEvent->addTags($additionalTags); $expected = CacheTags::fromElement($element)->with($additionalTag, $additionalTags); @@ -221,6 +222,34 @@ public function onDelete_should_invalidate_additional_tags_when_requested(Elemen ->shouldHaveBeenCalledOnce(); } + /** + * @test + * + * @dataProvider elementProvider + */ + public function onUpdate_should_dispatch_event_with_update_type(ElementEventInterface $event): void + { + $this->invalidateElementListener->onUpdate($event); + + $this->eventDispatcher->dispatch(Argument::that( + static fn (ElementInvalidationEvent $e) => InvalidationType::Update === $e->type, + ))->shouldHaveBeenCalledOnce(); + } + + /** + * @test + * + * @dataProvider elementProvider + */ + public function onDelete_should_dispatch_event_with_delete_type(ElementEventInterface $event): void + { + $this->invalidateElementListener->onDelete($event); + + $this->eventDispatcher->dispatch(Argument::that( + static fn (ElementInvalidationEvent $e) => InvalidationType::Delete === $e->type, + ))->shouldHaveBeenCalledOnce(); + } + public function elementProvider(): iterable { $asset = $this->prophesize(Asset::class);