diff --git a/CHANGELOG.md b/CHANGELOG.md index 4309824..f01ec3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - `ElasticsearchService::getItemById()` always returned null because it compared the hit total against an integer while Elasticsearch returns an object +- Deleting an entity together with its `LoggableChildInterface` children crashed the update handler: the child's delete log triggers an update log for the parent, whose identifier Doctrine has already cleared; such updates are now skipped ## [1.2.0] - 2026-07-02 diff --git a/src/MessageHandler/UpdateActivityLogHandler.php b/src/MessageHandler/UpdateActivityLogHandler.php index f1bb030..ab5d02c 100644 --- a/src/MessageHandler/UpdateActivityLogHandler.php +++ b/src/MessageHandler/UpdateActivityLogHandler.php @@ -31,6 +31,14 @@ public function __invoke(UpdateActivityLogMessageInterface $message): void return; } + // the item was removed in the meantime (e.g. a LoggableChildInterface + // logging to a parent that was deleted in the same flush); Doctrine + // clears generated identifiers on removal, and an item without an + // identifier has no current data tracker to compare against + if (null === $updatedItem->getId()) { + return; + } + $currentDataTracker = $this->currentDataTrackerProvider->getCurrentDataTrackerByClassAndId($message->getClassName(), $updatedItem->getId()); if (!$currentDataTracker instanceof CurrentDataTrackerInterface) { diff --git a/tests/UnitTests/MessageHandler/UpdateActivityLogHandlerTest.php b/tests/UnitTests/MessageHandler/UpdateActivityLogHandlerTest.php new file mode 100644 index 0000000..151cea7 --- /dev/null +++ b/tests/UnitTests/MessageHandler/UpdateActivityLogHandlerTest.php @@ -0,0 +1,44 @@ +createMock(LoggableContextCollectionFactoryInterface::class); + $collectionFactory->method('create')->willReturn( + new LoggableContextCollection([$item::class => ['groups' => ['test_log']]]) + ); + + $currentDataTrackerProvider = $this->createMock(CurrentDataTrackerProviderInterface::class); + $currentDataTrackerProvider->expects(self::never())->method('getCurrentDataTrackerByClassAndId'); + + $activityLogProcessor = $this->createMock(ActivityLogProcessorInterface::class); + $activityLogProcessor->expects(self::never())->method('processUpdatedItem'); + + $handler = new UpdateActivityLogHandler( + $activityLogProcessor, + $currentDataTrackerProvider, + new LoggableContextFactory($collectionFactory) + ); + + $handler(new UpdateActivityLogMessage($item)); + } +}