Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Eine eigene Custom-Operation: /projects/{id}/archive

Eine eigene Custom-Operation: /projects/{id}/archive

~16 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

Die SECHS Standard-Operationen (Kapitel 11) decken CRUD ab – eine Aktion wie "Projekt archivieren" ist WEDER Erstellen NOCH Ersetzen NOCH Löschen, sondern eine EIGENE, benannte Operation.

Das archived-Feld ergänzen

#[ORM\Column]
#[Groups(['project:read'])]
private bool $archived = false;

public function isArchived(): bool
{
    return $this->archived;
}

public function setArchived(bool $archived): static
{
    $this->archived = $archived;

    return $this;
}

BEWUSST OHNE project:writearchived soll NICHT über ein normales PATCH gesetzt werden können, SONDERN AUSSCHLIESSLICH über den dedizierten Endpunkt weiter unten.

Die Custom-Operation definieren

use ApiPlatform\Metadata\Post;
use App\State\ArchiveProjectProcessor;

new Post(
    uriTemplate: '/projects/{id}/archive',
    security: "is_granted('" . ProjectVoter::EDIT . "', object)",
    processor: ArchiveProjectProcessor::class,
    read: true,
),

read: true weist API Platform an, das Project ZUERST wie bei einem GET zu LADEN (über die {id} in der URL), BEVOR der Processor aufgerufen wird – der Voter-Check aus Kapitel 53 funktioniert dadurch GENAUSO wie bei den Standard-Operationen.

Den Processor implementieren

api/src/State/ArchiveProjectProcessor.php
<?php

declare(strict_types=1);

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\Project;
use Doctrine\ORM\EntityManagerInterface;

final class ArchiveProjectProcessor implements ProcessorInterface
{
    public function __construct(
        private readonly EntityManagerInterface $entityManager,
    ) {
    }

    public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Project
    {
        /** @var Project $data */
        $data->setArchived(true);
        $this->entityManager->flush();

        return $data;
    }
}

DANK read: true ist $data BEREITS das GELADENE Project-Objekt – setArchived(true) gefolgt von flush() reicht, KEIN eigener persist()-Aufruf nötig, da die Entity BEREITS von Doctrine VERWALTET wird.

Die Custom-Operation testen

curl -k -X POST https://localhost/api/projects/1/archive \
  -H "Authorization: Bearer $TOKEN"
{
  "id": 1,
  "name": "Website-Relaunch",
  "archived": true
}

Achtung: POST statt PATCH ist HIER BEWUSST gewählt: die Operation hat SEMANTISCH einen EIGENEN Namen ("archive", kein generisches "update") – eine GÄNGIGE REST-Konvention für Aktionen, die sich NICHT natürlich als reine Feldänderung ausdrücken lassen.

Tipp: GENAU dieses Muster (eigenes Feld OHNE write-Gruppe PLUS dedizierte Custom-Operation) eignet sich für JEDE "Aktion" statt "Zustandsänderung" – z. B. auch für /projects/{id}/restore als GEGENSTÜCK zu archive.