|ArrayObject|bool|float|int|string */ public function normalize(mixed $data, ?string $format = null, array $context = []): array|ArrayObject|bool|float|int|string|null { $context[self::ALREADY_CALLED] = true; /** @var null|array $normalized */ $normalized = $this->normalizer->normalize($data, $format, $context); if (is_array($normalized) && $data instanceof Document && $data->getId()) { // Remove raw 'path' if present (never expose it to the client) unset($normalized['path']); // Always provide URL-based access $normalized['fileUrl'] = '/api/documents/'.$data->getId().'/file'; $normalized['downloadUrl'] = '/api/documents/'.$data->getId().'/download'; } return $normalized; } public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool { if (isset($context[self::ALREADY_CALLED])) { return false; } return $data instanceof Document; } /** * @return array */ public function getSupportedTypes(?string $format): array { return [ Document::class => false, ]; } }