API Platform con Symfony nel 2026: Architettura e Domande per Colloqui Tecnici

Guida completa ad API Platform con Symfony nel 2026. Architettura REST API, State Providers, Processors e domande frequenti nei colloqui per sviluppatori Symfony.

API Platform con Symfony nel 2026: Architettura e Domande per Colloqui Tecnici

API Platform rappresenta il framework di riferimento per lo sviluppo di API REST e GraphQL con Symfony. Nel 2026, la versione attuale offre funzionalità avanzate come State Providers, State Processors e documentazione OpenAPI estesa. Questo articolo esplora l'architettura moderna di API Platform e prepara gli sviluppatori ai colloqui tecnici.

API Platform 4.x richiede Symfony 7.x e PHP 8.3+. Gli esempi presentati utilizzano le ultime funzionalità e le best practice per API pronte per la produzione.

Installazione e Configurazione del Progetto

La creazione di un nuovo progetto API Platform avviene tramite Composer. Il framework si integra perfettamente con applicazioni Symfony esistenti.

bash
composer create-project api-platform/api-platform my-api
cd my-api
composer require api-platform/core

La configurazione base in config/packages/api_platform.yaml definisce i parametri essenziali:

yaml
api_platform:
    title: "My API"
    version: "1.0.0"
    formats:
        jsonld: ["application/ld+json"]
        json: ["application/json"]
    docs_formats:
        jsonld: ["application/ld+json"]
        jsonopenapi: ["application/vnd.openapi+json"]
        html: ["text/html"]
    defaults:
        stateless: true
        cache_headers:
            vary: ["Content-Type", "Authorization", "Origin"]
        extra_properties:
            standard_put: true
            rfc_7807_compliant_errors: true

Definizione delle Entità come Risorse API

API Platform utilizza attributi PHP per configurare le risorse API. Un'entità tipica combina il mapping Doctrine con le definizioni API.

php
<?php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use ApiPlatform\Metadata\Delete;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Entity]
#[ApiResource(
    operations: [
        new GetCollection(),
        new Get(),
        new Post(security: "is_granted('ROLE_ADMIN')"),
        new Put(security: "is_granted('ROLE_ADMIN') or object.owner == user"),
        new Delete(security: "is_granted('ROLE_ADMIN')")
    ],
    paginationItemsPerPage: 20,
    order: ["createdAt" => "DESC"]
)]
class Product
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    #[Assert\NotBlank]
    #[Assert\Length(min: 3, max: 255)]
    private string $name;

    #[ORM\Column(type: "decimal", precision: 10, scale: 2)]
    #[Assert\Positive]
    private string $price;

    #[ORM\Column]
    private \DateTimeImmutable $createdAt;

    #[ORM\ManyToOne(targetEntity: User::class)]
    private ?User $owner = null;

    public function __construct()
    {
        $this->createdAt = new \DateTimeImmutable();
    }

    // Getters and setters...
}

State Providers per Logica Dati Personalizzata

Gli State Providers sostituiscono i precedenti Data Providers e offrono maggiore flessibilità nel recupero dei dati. Permettono l'integrazione di sorgenti dati esterne o logica di business complessa.

php
<?php

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Entity\Product;
use App\Repository\ProductRepository;
use Symfony\Component\HttpFoundation\RequestStack;

class ProductStateProvider implements ProviderInterface
{
    public function __construct(
        private ProductRepository $repository,
        private RequestStack $requestStack
    ) {}

    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
    {
        $request = $this->requestStack->getCurrentRequest();
        
        if ($operation instanceof GetCollection) {
            $category = $request?->query->get('category');
            
            if ($category) {
                return $this->repository->findByCategory($category);
            }
            
            return $this->repository->findAllActive();
        }

        return $this->repository->find($uriVariables['id']);
    }
}

La registrazione avviene tramite l'attributo provider:

php
#[ApiResource(
    provider: ProductStateProvider::class
)]
class Product
{
    // ...
}

State Processors per Operazioni di Scrittura

Gli State Processors gestiscono le richieste POST, PUT, PATCH e DELETE. Incapsulano la logica di business per le modifiche dei dati.

php
<?php

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\Product;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

class ProductStateProcessor implements ProcessorInterface
{
    public function __construct(
        private EntityManagerInterface $entityManager,
        private MailerInterface $mailer
    ) {}

    public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Product
    {
        if ($data instanceof Product && $operation instanceof Post) {
            $data->setCreatedAt(new \DateTimeImmutable());
            
            $this->entityManager->persist($data);
            $this->entityManager->flush();
            
            $this->sendNotification($data);
            
            return $data;
        }

        $this->entityManager->flush();
        
        return $data;
    }

    private function sendNotification(Product $product): void
    {
        $email = (new Email())
            ->to('admin@example.com')
            ->subject('New Product Created')
            ->text(sprintf('Product %s was created.', $product->getName()));
            
        $this->mailer->send($email);
    }
}

DTO e Trasformazioni Input/Output

I Data Transfer Objects permettono la separazione tra rappresentazione API ed entità interne. Questa architettura aumenta flessibilità e sicurezza.

php
<?php

namespace App\Dto;

use Symfony\Component\Validator\Constraints as Assert;

class CreateProductInput
{
    #[Assert\NotBlank]
    #[Assert\Length(min: 3, max: 255)]
    public string $name;

    #[Assert\NotBlank]
    #[Assert\Positive]
    public float $price;

    #[Assert\NotBlank]
    public string $category;

    public ?string $description = null;
}
php
<?php

namespace App\Dto;

class ProductOutput
{
    public int $id;
    public string $name;
    public float $price;
    public string $formattedPrice;
    public string $category;
    public string $createdAt;
}

La configurazione della trasformazione avviene nella definizione della risorsa:

php
#[ApiResource(
    operations: [
        new Post(
            input: CreateProductInput::class,
            output: ProductOutput::class,
            processor: CreateProductProcessor::class
        )
    ]
)]
class Product
{
    // ...
}

Filtri e Ordinamento

API Platform offre filtri dichiarativi per pattern di query comuni. La configurazione avviene direttamente sull'entità.

php
<?php

use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Doctrine\Orm\Filter\RangeFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Metadata\ApiFilter;

#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: [
    'name' => 'partial',
    'category.name' => 'exact'
])]
#[ApiFilter(RangeFilter::class, properties: ['price'])]
#[ApiFilter(DateFilter::class, properties: ['createdAt'])]
#[ApiFilter(OrderFilter::class, properties: ['name', 'price', 'createdAt'])]
class Product
{
    // ...
}

Un esempio di richiesta API con filtri:

bash
GET /api/products?name=laptop&price[gte]=500&order[price]=asc

Autenticazione e Autorizzazione

La configurazione della sicurezza utilizza il componente Security di Symfony. API Platform integra Voter ed expressions in modo trasparente.

php
<?php

namespace App\Security\Voter;

use App\Entity\Product;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;

class ProductVoter extends Voter
{
    public const EDIT = 'PRODUCT_EDIT';
    public const DELETE = 'PRODUCT_DELETE';

    protected function supports(string $attribute, mixed $subject): bool
    {
        return in_array($attribute, [self::EDIT, self::DELETE])
            && $subject instanceof Product;
    }

    protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
    {
        $user = $token->getUser();

        if (!$user instanceof UserInterface) {
            return false;
        }

        /** @var Product $product */
        $product = $subject;

        return match($attribute) {
            self::EDIT => $this->canEdit($product, $user),
            self::DELETE => $this->canDelete($product, $user),
            default => false,
        };
    }

    private function canEdit(Product $product, UserInterface $user): bool
    {
        return $product->getOwner() === $user || in_array('ROLE_ADMIN', $user->getRoles());
    }

    private function canDelete(Product $product, UserInterface $user): bool
    {
        return in_array('ROLE_ADMIN', $user->getRoles());
    }
}

Versionamento delle API

API Platform supporta diverse strategie di versionamento. Il versionamento basato su URL risulta essere il più diffuso.

php
#[ApiResource(
    routePrefix: '/v1',
    operations: [
        new GetCollection(),
        new Get()
    ]
)]
class Product
{
    // ...
}

#[ApiResource(
    routePrefix: '/v2',
    operations: [
        new GetCollection(),
        new Get()
    ],
    normalizationContext: ['groups' => ['product:read:v2']]
)]
class ProductV2
{
    // ...
}

Domande Frequenti nei Colloqui

Domanda: Qual è la differenza tra State Provider e State Processor?

Gli State Providers gestiscono le operazioni di lettura (richieste GET) e recuperano dati da qualsiasi sorgente. Gli State Processors elaborano le operazioni di scrittura (POST, PUT, PATCH, DELETE) e implementano la logica di persistenza ed eventuali effetti collaterali come le notifiche.

Domanda: Come si implementa la paginazione in API Platform?

API Platform offre paginazione automatica. La configurazione può essere globale o per singola risorsa:

php
#[ApiResource(
    paginationEnabled: true,
    paginationItemsPerPage: 30,
    paginationMaximumItemsPerPage: 100,
    paginationClientEnabled: true
)]

Domanda: Quali gruppi di serializzazione sono consigliati?

Le best practice raccomandano gruppi separati per lettura e scrittura:

php
#[ApiResource(
    normalizationContext: ['groups' => ['product:read']],
    denormalizationContext: ['groups' => ['product:write']]
)]

Domanda: Come si testano gli endpoint di API Platform?

php
<?php

namespace App\Tests\Api;

use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Entity\Product;
use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait;

class ProductTest extends ApiTestCase
{
    use RefreshDatabaseTrait;

    public function testGetCollection(): void
    {
        $response = static::createClient()->request('GET', '/api/products');

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains(['@type' => 'hydra:Collection']);
    }

    public function testCreateProduct(): void
    {
        $response = static::createClient()->request('POST', '/api/products', [
            'json' => [
                'name' => 'Test Product',
                'price' => '29.99'
            ],
            'headers' => [
                'Authorization' => 'Bearer ' . $this->getToken()
            ]
        ]);

        $this->assertResponseStatusCodeSame(201);
        $this->assertJsonContains(['name' => 'Test Product']);
    }
}

Pronto a superare i tuoi colloqui su Symfony?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Conclusione

API Platform nel 2026 offre un'architettura matura per lo sviluppo di API REST con Symfony. State Providers e Processors permettono pattern di accesso ai dati flessibili, mentre i DTO garantiscono una separazione pulita tra API e logica di dominio. L'integrazione di filtri, sicurezza e documentazione automatica rende API Platform la scelta principale per lo sviluppo professionale di API. I concetti e le domande trattate preparano al meglio gli sviluppatori per i colloqui tecnici e trasmettono le best practice per implementazioni pronte per la produzione.

Sfida del giorno

Sapresti trovare il bug in Symfony?

Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Anthony Fillion-Maillet

Scritto da

Anthony Fillion-Maillet

Fondatore di SharpSkill

Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.

Aggiornato il 8 settembre 2026

Condividi

Articoli correlati