While creating the API interface for the activity entity, I needed to display the path to the API file.
I use the Sonata Media Bundle for file management.
src/Entity/Web/AdditionalActivity.php
class AdditionalActivity
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column]
#[Assert\NotNull]
private ?bool $isActive = false;
#[ORM\Column(length: 255)]
#[Assert\NotBlank]
#[Assert\Length(max: 255)]
#[Groups(['additional-activity:read'])]
private ?string $title = null;
#[ORM\ManyToOne]
#[Groups(['additional-activity:read'])]
private ?SonataMediaMedia $imageMedia1 = null;I marked the fields to be serialized with the additional-activity:read group.
My controller is very simple: it is supposed to fetch all the entities and serialize them to JSON.
src/Controller/Api/Web/AdditionalActivityController.php
<?php
namespace App\Controller\Api\Web;
use App\Repository\Web\AdditionalActivityRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class AdditionalActivityController extends AbstractController
{
#[Route(
'/api/additional-activities',
name: 'api_additional_activities',
methods: ['GET'],
format: 'json',
)]
public function index(
Request $request,
AdditionalActivityRepository $additionalActivityRepository,
): Response {
$additionalActivities = $additionalActivityRepository->findAll();
return $this->json([
'data' => $additionalActivities,
], Response::HTTP_OK, [], [
'groups' => ['additional-activity:read'],
]);
}
}Result from API:
{
"data": [
{
"title": "Dodatkowa aktywność 1",
"imageMedia1": [],
},
{
"title": "Alias et quia qui reprehenderit iure pariatur itaque.",
"imageMedia1": null,
},
]
}As we can see, the file path is missing for the imageMedia1 field. We will resolve this issue using a dedicated serializer. To generate the path, I will use the built-in: path function found in the SonataMediaBundle file:
vendor/sonata-project/media-bundle/src/Twig/MediaRuntime.php
The path will be created only for the additional-activity:read group. Additional ones can be added here if necessary.
src/Serializer/MediaSerializer.php
<?php
namespace App\Serializer;
use App\Entity\SonataMediaMedia;
use Sonata\MediaBundle\Twig\MediaRuntime;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class MediaSerializer implements NormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
private const string ALREADY_CALLED = 'MEDIA_NORMALIZER_ALREADY_CALLED';
public function __construct(
#[Autowire(service: 'sonata.media.twig.runtime')]
private MediaRuntime $mediaRuntime,
) {
}
/**
* @return array<string, mixed>
*/
public function normalize(mixed $object, ?string $format = null, array $context = []): array
{
/** @var SonataMediaMedia $object */
$context[self::ALREADY_CALLED] = true;
$data = $this->normalizer->normalize($object, $format, $context);
if (!empty(array_intersect($context['groups'] ?? [], ['additional-activity:read']))) {
$data['referencePathname'] = $this->mediaRuntime->path($object, 'reference');
}
return $data;
}
/**
* @param array<string, mixed> $context
*/
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
{
if (!$data instanceof SonataMediaMedia) {
return false;
}
// avoid recursion: only call once per object
if (isset($context[self::ALREADY_CALLED])) {
return false;
}
return true;
}
/**
* @return array<class-string, bool>
*/
public function getSupportedTypes(?string $format): array
{
return [SonataMediaMedia::class => false]; // low memory error when true
}
}
Result from API:
{
"data": [
{
"title": "Dodatkowa aktywność 1",
"imageMedia1": {
"referencePathname": "/uploads/media/additional_activity/0001/01/d5da05061cbc5cda9103114fef87ee3de34175ee.jpg"
},
},
{
"title": "Alias et quia qui reprehenderit iure pariatur itaque.",
"imageMedia1": null,
},
]
}

Leave a Reply