KwikTech Symfony · API Platform · Doctrine upgrades
Upgrade-Leitfaden · 10 Min. Lesezeit

API Platform 3 auf 4: der State-basierte Ablauf, RFC-7807-Fehler und Hydra-Änderungen

Die Kompatibilitäts-Flags von 3.4 einzeln umstellen, weg von den Event-Listenern, das neue Fehlerformat und die Hydra-Keys ohne Präfix übernehmen, dann auf 4.x wechseln.

Veröffentlicht 2026-09-12. Geschrieben für Teams, die das Upgrade selbst machen; wer es lieber machen lässt, findet hier Upgrades zum Festpreis. Auch verfügbar auf English.

Was 4.0 entfernt hat

API Platform 4 ist die Version, in der die 2.x-Kompatibilitätsschichten endgültig verschwinden. Der Event-Listener-basierte Request-Ablauf, der Legacy-Inflector, das Fehlerformat vor RFC 7807 und die hydra:-präfixierten Collection-Keys waren in 3.x alle umschaltbar und sind in 4 weg oder standardmäßig aus. 4.x setzt Symfony 6.4 oder neuer und PHP 8.2 oder neuer voraus; 4.3 ist der gepflegte Bugfix-Zweig und 4.2 der Security-Zweig. Alles darunter ist nicht mehr gepflegt.

Die Methode ist dieselbe wie bei 2 auf 3: auf das letzte 3.x (3.4) gehen, jedes Kompatibilitäts-Flag einzeln auf seinen 4.x-Wert stellen, reparieren, was bricht, dann springen. Jedes Flag ist ein Pull Request. Nichts hier braucht ein Big-Bang-Release.

Schritt 1: 3.4 und null Deprecations

composer require api-platform/core:^3.4 --with-all-dependencies

Testsuite mit der PHPUnit-Bridge ausführen (SYMFONY_DEPRECATIONS_HELPER=max[self]=0). API Platform 3.4 deprecated alles, was 4.0 entfernt; der Report ist Ihre Liste.

Schritt 2: der State-basierte Request-Ablauf

In 2.x und frühem 3.x lief ein Request durch eine Kette von Symfony-Event-Listenern (read, deserialize, validate, write, serialize) mit festen Prioritäten, und Projekte hängten ihre Logik an EventPriorities::PRE_WRITE und Co. 3.2 führte den State-basierten Ablauf ein, in dem dieselben Schritte Provider und Processor sind, die um Ihre Operation herum komponiert werden, und 4.0 macht ihn zum einzigen Ablauf.

# config/packages/api_platform.yaml (3.2+)
api_platform:
    use_symfony_listeners: false    # 4.x-Default; true behält vorerst die alten Listener

Umstellen und Tests ausführen. Jeder Listener, den Ihr Projekt auf den Events kernel.view oder kernel.request mit API-Platform-Prioritäten registriert hat, braucht jetzt ein neues Zuhause:

// Vorher: Listener auf PRE_WRITE
final class OrderSubmitListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [KernelEvents::VIEW => ['onView', EventPriorities::PRE_WRITE]];
    }

    public function onView(ViewEvent $event): void
    {
        $order = $event->getControllerResult();
        if (!$order instanceof Order || !$event->getRequest()->isMethod('POST')) {
            return;
        }
        $order->submittedAt = new \DateTimeImmutable();
    }
}

// Nachher: ein Processor, der den Persist-Processor für diese Operation dekoriert
#[Post(processor: OrderSubmitProcessor::class)]
class Order {}

final class OrderSubmitProcessor implements ProcessorInterface
{
    public function __construct(
        #[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')] private ProcessorInterface $persist,
    ) {}

    public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
    {
        $data->submittedAt = new \DateTimeImmutable();

        return $this->persist->process($data, $operation, $uriVariables, $context);
    }
}

Listener, die die Response berührten (Header hinzufügen, Ausgabe wrappen), werden zu einem Processor nach der Serialisierung oder zu einem normalen Symfony-Response-Listener ohne API-Platform-Prioritäten. Listener, die Autorisierung machten, sollten längst security:-Expressions oder Voter sein; falls nicht, ist jetzt der Moment.

Schritt 3: RFC-7807-Fehler

4.x liefert Fehler als application/problem+json (und die JSON-LD- und Hydra-Varianten) mit type, title, status, detail. Validierungsfehler tragen ein violations-Array. Zuerst auf 3.x einschalten:

api_platform:
    defaults:
        rfc_7807_compliant_errors: true

Was sich für Clients ändert: Der @type eines Fehlers ist Error oder ConstraintViolation (vorher hydra:Error und ConstraintViolationList), hydra:description wird zu detail, und der HTTP-Status eines Validierungsfehlers bleibt 422. Eigene Exception-zu-Status-Zuordnungen in api_platform.exception_to_status funktionieren weiter. Wer einen eigenen Error-Normalizer hatte, kann ihn fast sicher durch eine Resource im Stil von ErrorResource oder durch ProblemExceptionInterface auf den eigenen Exceptions ersetzen.

final class InsufficientStockException extends \RuntimeException implements ProblemExceptionInterface
{
    public function getType(): string { return '/errors/insufficient-stock'; }
    public function getTitle(): ?string { return 'Insufficient stock'; }
    public function getStatus(): ?int { return 409; }
    public function getDetail(): ?string { return $this->getMessage(); }
    public function getInstance(): ?string { return null; }
}

Schritt 4: Hydra-Keys ohne Präfix

Collections serialisieren in 4.x als member, totalItems, view und search statt hydra:member, hydra:totalItems und so weiter. Der @context deklariert weiterhin das Hydra-Vokabular, JSON-LD-Konsumenten sind also nicht betroffen; reine JSON-Konsumenten, die hydra:member fest verdrahtet haben, schon.

api_platform:
    serializer:
        hydra_prefix: false    # 4.x-Default; auf true setzen, um die alten Keys während eines Übergangs zu behalten

Auf 3.4 umstellen, Frontend und API-Tests anpassen, ausliefern. Wenn Ihre API externe Konsumenten hat, die Sie nicht kontrollieren, behalten Sie hydra_prefix: true in 4.x so lange wie nötig; die Option gibt es dort weiterhin.

Schritt 5: die kleineren Flags

Option (3.x)4.xBedeutung
keep_legacy_inflector: truefalse / entferntResource-Kurznamen werden mit symfony/string pluralisiert. Eine Resource namens Person war in beiden /people, aber unregelmäßige Namen können sich ändern; debug:router prüfen.
event_listeners_backward_compatibility_layerentfernt2.x-Listener-Verhalten; durch Schritt 2 abgelöst.
enable_docs, enable_entrypoint, enable_swagger_uiunverändertGibt es weiterhin.
defaults.stateless: trueunverändertBehalten; das schaltet sessionbasiertes Firewall-Verhalten für API-Routen ab.

Schritt 6: Packages und Namespaces

4.x hat den Monolithen in Komponenten aufgeteilt. api-platform/core installiert weiterhin alles, und dabei zu bleiben ist in Ordnung. Wer den kleineren Fußabdruck will: Die Symfony-Integration ist api-platform/symfony plus die genutzten Teile (api-platform/doctrine-orm, api-platform/graphql, api-platform/openapi). Namespaces für Anwendungscode sind nicht umgezogen: ApiPlatform\Metadata\*, ApiPlatform\State\* und ApiPlatform\Doctrine\Orm\Filter\* sind in 3.4 und 4.3 gleich.

Doctrine ORM 3 wird von 4.x unterstützt (jedes 3.x außer exakt 3.0.0, das ausgeschlossen ist). GraphQL wechselt auf webonyx/graphql-php 15; eigene GraphQL-Resolver, die QueryItemResolverInterface implementieren, funktionieren weiter.

Schritt 7: Sprung

composer require api-platform/core:^4.3 --with-all-dependencies
bin/console cache:clear
bin/console debug:router | grep api_       # gegen einen Dump von vorher vergleichen
vendor/bin/phpunit

Entfernen Sie die in Schritt 2 bis 5 gesetzten Flags aus der Konfiguration, sie sind jetzt die Defaults. Erzeugen Sie das OpenAPI-Dokument neu (bin/console api:openapi:export --yaml) und vergleichen Sie es mit dem Export aus 3.4: Das fängt umbenannte Operationen und geänderte Fehlerschemata ab, bevor ein Client es tut.

Reihenfolge der Pull Requests

  1. 3.4, Deprecations auf null
  2. use_symfony_listeners: false, Listener als Provider/Processor umgeschrieben
  3. rfc_7807_compliant_errors: true, Clients und Tests angepasst
  4. hydra_prefix: false, Frontend angepasst
  5. Inflector und restliche Flags
  6. Sprung auf 4.3, OpenAPI-Diff

Zusätzlich zu einem Symfony-Upgrade landet das für die meisten APIs bei €3.000 bis €6.000; Treiber sind die Zahl der eigenen Listener und der Konsumenten, die Fehlerbodies parsen.

Noch auf API Platform 2 oder 3?

Der Scan markiert API Platform unter 4 als Cliff, zeigt, welche Symfony-Versionen Ihr installiertes Release erlaubt, und ordnet die API-Platform-Schritte korrekt relativ zu den Symfony- und Doctrine-Schritten ein.

Mein Projekt scannen Assessment anfragen (€750)

Weitere Leitfäden