*/ private array $versions = []; public function __construct( private readonly Publisher $publisher, private readonly CorrelationContext $correlationContext, private readonly NormalizerInterface $normalizer, ) { } public function publishEntityChange(object $entity, string $op): void { $resource = $this->resolveResource($entity); if (null === $resource) { return; // not a #[BridgeResource] } $name = $resource->name ?? self::deriveName($entity::class); $id = (string) $this->extractId($entity); $envelope = [ 'op' => $op, 'id' => $id, 'version' => $this->nextVersion($name), 'data' => 'delete' === $op ? null : $this->normalize($entity), ]; if (null !== $key = $this->correlationContext->get()) { $envelope['correlationKey'] = $key; } $this->publisher->publish("app://model/{$name}", $envelope); $this->publisher->publish("app://model/{$name}/{$id}", $envelope); } private function resolveResource(object $entity): ?BridgeResource { $reflection = new \ReflectionClass($entity); $attrs = $reflection->getAttributes(BridgeResource::class); if ([] === $attrs) { return null; } return $attrs[0]->newInstance(); } private function extractId(object $entity): mixed { if (method_exists($entity, 'getId')) { return $entity->getId(); } $r = new \ReflectionClass($entity); if ($r->hasProperty('id')) { $prop = $r->getProperty('id'); $prop->setAccessible(true); return $prop->getValue($entity); } throw new \LogicException(\sprintf('Cannot extract id from %s: no getId() method or $id property.', $entity::class)); } /** * @return array */ private function normalize(object $entity): array { /** @var array $data */ $data = $this->normalizer->normalize($entity, 'json', [ 'circular_reference_handler' => static fn (object $o): string => (string) ($o->getId() ?? ''), ]); return $data; } private function nextVersion(string $name): int { if (!isset($this->versions[$name])) { $this->versions[$name] = (int) (microtime(true) * 1_000_000); } return ++$this->versions[$name]; } private static function deriveName(string $fqcn): string { $basename = substr($fqcn, (int) strrpos($fqcn, '\\') + 1); return strtolower($basename); } }