From ee2ee36db3746b9609dc35ddae9e64b87c53ef2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Poirier=20Th=C3=A9or=C3=AAt?= Date: Thu, 12 Dec 2024 23:18:26 -0500 Subject: [PATCH] [Messenger] DoctrineEnvelopeEntityReference custom exception flow --- app/src/Message/NewUserMessage.php | 11 +++- .../PropertyReferenceEncodingListener.php | 47 ++++++++++---- .../Exception/ObjectNotFoundException.php | 38 +++++++++++ .../PropertyReferenceEncodingListenerTest.php | 47 -------------- .../PropertyReferenceEncodingListenerTest.php | 64 ++++++++++++++----- 5 files changed, 129 insertions(+), 78 deletions(-) create mode 100644 packages/messenger/DoctrineEnvelopeEntityReference/Exception/ObjectNotFoundException.php delete mode 100644 packages/messenger/Tests/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListenerTest.php diff --git a/app/src/Message/NewUserMessage.php b/app/src/Message/NewUserMessage.php index 67075b8e..c6494f23 100644 --- a/app/src/Message/NewUserMessage.php +++ b/app/src/Message/NewUserMessage.php @@ -4,19 +4,28 @@ use App\Entity\User; use Draw\Component\Messenger\AutoStamp\Message\StampingAwareInterface; +use Draw\Component\Messenger\DoctrineEnvelopeEntityReference\Exception\ObjectNotFoundException; use Draw\Component\Messenger\DoctrineEnvelopeEntityReference\Message\DoctrineReferenceAwareInterface; +use Draw\Component\Messenger\DoctrineEnvelopeEntityReference\Stamp\PropertyReferenceStamp; use Draw\Component\Messenger\Message\AsyncLowPriorityMessageInterface; use Draw\Component\Messenger\Searchable\Stamp\SearchableTagStamp; use Symfony\Component\Messenger\Envelope; class NewUserMessage implements DoctrineReferenceAwareInterface, AsyncLowPriorityMessageInterface, StampingAwareInterface { - public function __construct(private ?User $user) + private PropertyReferenceStamp|User|null $user; + + public function __construct(User $user) { + $this->user = $user; } public function getUser(): User { + if ($this->user instanceof PropertyReferenceStamp) { + throw new ObjectNotFoundException(User::class, $this->user); + } + return $this->user; } diff --git a/packages/messenger/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListener.php b/packages/messenger/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListener.php index 838a1837..49031203 100644 --- a/packages/messenger/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListener.php +++ b/packages/messenger/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListener.php @@ -5,32 +5,25 @@ use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ObjectManager; use Draw\Component\Core\Reflection\ReflectionAccessor; +use Draw\Component\Core\Reflection\ReflectionExtractor; use Draw\Component\Messenger\DoctrineEnvelopeEntityReference\Message\DoctrineReferenceAwareInterface; use Draw\Component\Messenger\DoctrineEnvelopeEntityReference\Stamp\PropertyReferenceStamp; use Draw\Component\Messenger\SerializerEventDispatcher\Event\BaseSerializerEvent; use Draw\Component\Messenger\SerializerEventDispatcher\Event\PostDecodeEvent; use Draw\Component\Messenger\SerializerEventDispatcher\Event\PostEncodeEvent; use Draw\Component\Messenger\SerializerEventDispatcher\Event\PreEncodeEvent; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\Attribute\AsEventListener; use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp; -class PropertyReferenceEncodingListener implements EventSubscriberInterface +class PropertyReferenceEncodingListener { - public static function getSubscribedEvents(): array - { - return [ - PreEncodeEvent::class => 'createPropertyReferenceStamps', - PostEncodeEvent::class => 'restoreDoctrineObjects', - PostDecodeEvent::class => 'restoreDoctrineObjects', - ]; - } - public function __construct( private ?ManagerRegistry $ormManagerRegistry, private ?ManagerRegistry $odmManagerRegistry, ) { } + #[AsEventListener] public function createPropertyReferenceStamps(PreEncodeEvent $event): void { $envelope = $event->getEnvelope(); @@ -79,6 +72,10 @@ public function createPropertyReferenceStamps(PreEncodeEvent $event): void $event->setEnvelope($envelope->with(...$stamps)); } + #[ + AsEventListener(event: PostEncodeEvent::class), + AsEventListener(event: PostDecodeEvent::class) + ] public function restoreDoctrineObjects(BaseSerializerEvent $event): void { $message = $event->getEnvelope()->getMessage(); @@ -90,11 +87,35 @@ public function restoreDoctrineObjects(BaseSerializerEvent $event): void $stamps = $event->getEnvelope()->all(PropertyReferenceStamp::class); foreach ($stamps as $stamp) { + $object = $this->getManagerForClass($stamp->getClass()) + ->find($stamp->getClass(), $stamp->getIdentifiers()) + ; + + if ($object) { + ReflectionAccessor::setPropertyValue( + $message, + $stamp->getPropertyName(), + $object + ); + + continue; + } + + $propertyReflection = ReflectionAccessor::getPropertyReflection( + $message, + $stamp->getPropertyName(), + ); + + $classes = ReflectionExtractor::getClasses($propertyReflection->getType()); + + if (!\in_array(PropertyReferenceStamp::class, $classes, true)) { + continue; + } + ReflectionAccessor::setPropertyValue( $message, $stamp->getPropertyName(), - $this->getManagerForClass($stamp->getClass()) - ->find($stamp->getClass(), $stamp->getIdentifiers()) + $stamp, ); } } diff --git a/packages/messenger/DoctrineEnvelopeEntityReference/Exception/ObjectNotFoundException.php b/packages/messenger/DoctrineEnvelopeEntityReference/Exception/ObjectNotFoundException.php new file mode 100644 index 00000000..6c999e54 --- /dev/null +++ b/packages/messenger/DoctrineEnvelopeEntityReference/Exception/ObjectNotFoundException.php @@ -0,0 +1,38 @@ +getIdentifiers()) + ); + } + + parent::__construct($message); + } + + public function getObjectClass(): string + { + return $this->objectClass; + } + + public function getPropertyReferenceStamp(): ?PropertyReferenceStamp + { + return $this->propertyReferenceStamp; + } +} diff --git a/packages/messenger/Tests/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListenerTest.php b/packages/messenger/Tests/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListenerTest.php deleted file mode 100644 index a2ca0a64..00000000 --- a/packages/messenger/Tests/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListenerTest.php +++ /dev/null @@ -1,47 +0,0 @@ -object = new PropertyReferenceEncodingListener( - $this->createMock(ManagerRegistry::class), - $this->createMock(ManagerRegistry::class) - ); - } - - public function testConstruct(): void - { - static::assertInstanceOf( - EventSubscriberInterface::class, - $this->object, - ); - } - - public function testGetSubscribedEvents(): void - { - static::assertSame( - [ - PreEncodeEvent::class => 'createPropertyReferenceStamps', - PostEncodeEvent::class => 'restoreDoctrineObjects', - PostDecodeEvent::class => 'restoreDoctrineObjects', - ], - $this->object::getSubscribedEvents() - ); - } -} diff --git a/tests/Messenger/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListenerTest.php b/tests/Messenger/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListenerTest.php index e1ea9b39..911f4f65 100644 --- a/tests/Messenger/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListenerTest.php +++ b/tests/Messenger/DoctrineEnvelopeEntityReference/EventListener/PropertyReferenceEncodingListenerTest.php @@ -9,13 +9,14 @@ use Doctrine\ORM\EntityManagerInterface; use Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire\AutowireService; use Draw\Component\Core\Reflection\ReflectionAccessor; +use Draw\Component\Messenger\DoctrineEnvelopeEntityReference\Exception\ObjectNotFoundException; use Draw\Component\Messenger\SerializerEventDispatcher\Event\PostEncodeEvent; use Draw\Component\Messenger\SerializerEventDispatcher\Event\PreEncodeEvent; use Draw\Component\Tester\PHPUnit\Extension\SetUpAutowire\AutowiredInterface; use Draw\Contracts\Messenger\EnvelopeFinderInterface; use PHPUnit\Framework\Attributes\Depends; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * @internal @@ -25,23 +26,25 @@ class PropertyReferenceEncodingListenerTest extends KernelTestCase implements Au #[AutowireService] private EnvelopeFinderInterface $envelopeFinder; + #[AutowireService] + private EntityManagerInterface $entityManager; + + #[AutowireService] + private EventDispatcherInterface $eventDispatcher; + private bool $preEncodeEventCalled = false; private bool $postEncodeEventCalled = false; - private static string $email; + private static User $user; public function testSend(): void { - $container = static::getContainer(); - - $entityManager = $container->get(EntityManagerInterface::class); - $eventDispatcher = $container->get(EventDispatcherInterface::class); + self::$user = (new User()) + ->setEmail(uniqid().'@example.com') + ; - $user = new User(); - $user->setEmail(self::$email = uniqid().'@example.com'); - - $eventDispatcher->addListener( + $this->eventDispatcher->addListener( PreEncodeEvent::class, function (PreEncodeEvent $event): void { $message = $event->getEnvelope()->getMessage(); @@ -62,9 +65,9 @@ function (PreEncodeEvent $event): void { -1 ); - $eventDispatcher->addListener( + $this->eventDispatcher->addListener( PostEncodeEvent::class, - function (PostEncodeEvent $event) use ($user): void { + function (PostEncodeEvent $event): void { $message = $event->getEnvelope()->getMessage(); if (!$message instanceof NewUserMessage) { return; @@ -73,7 +76,7 @@ function (PostEncodeEvent $event) use ($user): void { $this->postEncodeEventCalled = true; static::assertSame( - $user, + self::$user, ReflectionAccessor::getPropertyValue( $message, 'user' @@ -84,9 +87,9 @@ function (PostEncodeEvent $event) use ($user): void { -1 ); - $entityManager->persist($user); + $this->entityManager->persist(self::$user); - $entityManager->flush(); + $this->entityManager->flush(); static::assertTrue($this->preEncodeEventCalled); @@ -96,18 +99,45 @@ function (PostEncodeEvent $event) use ($user): void { #[Depends('testSend')] public function testLoad(): void { - $envelope = $this->envelopeFinder->findByTags([self::$email])[0]; + $envelope = $this->envelopeFinder->findByTags([self::$user->getEmail()])[0]; $message = $envelope->getMessage(); static::assertInstanceOf(NewUserMessage::class, $message); static::assertSame( - self::$email, + self::$user->getEmail(), $message->getUser()->getEmail() ); } + #[Depends('testSend')] + public function testLoadNotFound(): void + { + $this->entityManager + ->getConnection() + ->delete('draw_acme__user', ['email' => self::$user->getEmail()]) + ; + + $envelope = $this->envelopeFinder->findByTags([self::$user->getEmail()])[0]; + + $message = $envelope->getMessage(); + + static::assertInstanceOf(NewUserMessage::class, $message); + + $this->expectException(ObjectNotFoundException::class); + + $this->expectExceptionMessage( + \sprintf( + 'Object of class [%s] not found. Identifiers [%s]', + User::class, + json_encode(['id' => self::$user->getId()]), + ) + ); + + $message->getUser(); + } + public function testODM(): void { $testDocument = new TestDocument();