Skill state-processor
Crée des processeurs d'état pour gérer la persistance et les effets de bord lors des opérations d'écriture telles que POST, PUT, PATCH et DELETE.
- Étoiles GitHub
- 29
- Fichiers
- 1
- Dernier commit
- 22 oct. 2026
- Licence
- —
- Outils autorisés
- —
Que fait la skill state-processor ?
state-processor est une agent skill. Crée des processeurs d'état pour gérer la persistance et les effets de bord lors des opérations d'écriture telles que POST, PUT, PATCH et DELETE.
Résumé généré à partir du SKILL.md de la skill.
À propos du skill state-processor
Cette skill permet de gérer les opérations d'écriture et les effets de bord dans API Platform. Elle permet aux développeurs de créer des state processors qui gèrent le côté Command des opérations de type CQRS, spécifiquement pour les requêtes POST, PUT, PATCH et DELETE. Lorsqu'un agent doit implémenter une logique de persistance personnalisée, comme le hachage de mots de passe, la création d'entités liées ou le déclenchement de notifications par e-mail, il peut utiliser cette skill pour définir comment les données sont traitées durant la phase d'écriture.
Les développeurs peuvent utiliser ces processeurs pour décorer des services intégrés comme le processeur de persistance Doctrine ORM ou le processeur de suppression MongoDB ODM. Cette approche permet d'ajouter de la logique avant ou après le cycle de persistance standard. La skill prend également en charge des cas d'utilisation avancés, comme l'implémentation d'une fonctionnalité de soft-delete ou le retour d'objets Symfony Response pour les téléchargements de fichiers en réglant le flag write sur true pour les opérations GET. Elle fonctionne avec différents frameworks, notamment Symfony et Laravel Eloquent, en utilisant l'interface ProcessorInterface.
Ce qu'un agent peut faire avec
- 1Implémenter des effets de bord personnalisésAjouter une logique pour envoyer une notification par e-mail immédiatement après qu'une commande a été persistée avec succès.
- 2Gérer la logique de soft-deleteCréer un processeur réutilisable qui marque une ressource comme supprimée au lieu de la retirer de la base de données.
- 3Générer des téléchargements de fichiersConfigurer une opération GET pour déclencher un processeur qui retourne une réponse de fichier binaire.
- 4Automatiser la création d'entités liéesGarantir que les ressources enfants, comme les boîtes de réception, sont automatiquement créées chaque fois qu'un nouveau compte est enregistré.
Rédigé à partir du SKILL.md du skill. Chaque cas d'utilisation est vérifié par rapport à une citation de celui-ci.
Comment installer la skill state-processor
Chaque commande copie le dossier de la skill dans le répertoire des skills du client. Redémarrez le client ou démarrez une nouvelle session pour la charger.Installer la skill state-processor dans Plugin
/plugin marketplace add api-platform/skillset /plugin install api-platform@api-platform-skillset
Installer la skill state-processor dans Claude Code
~/.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processorInstaller la skill state-processor dans Claude Code (project)
.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor .claude/skills/state-processorInstaller la skill state-processor dans Codex
~/.agents/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.agents/skills/state-processorInstaller la skill state-processor dans Gemini CLI
~/.gemini/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.gemini/skills/state-processorInstaller la skill state-processor dans Claude.ai
npx degit api-platform/skillset/skills/state-processor state-processor && zip -r state-processor.zip state-processor # claude.ai → Customize → Skills → + → Create skill → Upload a skill → state-processor.zip
SKILL.md
Voir sur GitHubCreating State Processors
State Processors handle persistence and side effects for write operations (Post, Patch, Put, Delete) — the write phase of an operation, the Command side of API Platform's CQRS-style split (a state-provider handles the Query/read side).
A processor runs when the operation's write flag is true. You normally leave it
unset and API Platform resolves it from the request: write defaults to "the HTTP
method is not safe", and read (the provider) to "the operation has URI
variables, or the method is safe".
| Operation | processor runs (write) |
provider runs (read) |
|---|---|---|
Get (item) / GetCollection |
no | yes |
Post (collection) |
yes | no (no URI vars, unsafe) |
Put, Patch, Delete (item) |
yes | yes |
A Get defaults to write: false, so its processor never fires until you set
write: true (file downloads, report generation — see Returning HTTP Responses
below). See operations → Read & write phases for the full read → deserialize → validate → write → serialize lifecycle.
Basic Structure
<?php
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
/**
* @implements ProcessorInterface<YourResource, YourResource>
*/
final class YourProcessor implements ProcessorInterface
{
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
// Business logic + persistence
return $data;
}
}
Decorating Built-in Processors
Wrap the default processor to add side effects before/after persistence:
use Symfony\Component\DependencyInjection\Attribute\Autowire;
final class OrderCreateProcessor implements ProcessorInterface
{
public function __construct(
#[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
private ProcessorInterface $persistProcessor,
private MailerInterface $mailer,
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
// Pre-persistence: set relationships, hash passwords, etc.
$data->setCreatedBy($this->getUser());
$result = $this->persistProcessor->process($data, $operation, $uriVariables, $context);
// Post-persistence: send notifications, trigger events
$this->mailer->send(new OrderConfirmation($result));
return $result;
}
}
Doctrine service names
| Persistence | Persist Processor | Remove Processor |
|---|---|---|
| ORM | api_platform.doctrine.orm.state.persist_processor |
api_platform.doctrine.orm.state.remove_processor |
| MongoDB ODM | api_platform.doctrine_mongodb.odm.state.persist_processor |
api_platform.doctrine_mongodb.odm.state.remove_processor |
Laravel (Eloquent):
ProcessorInterfaceis identical and resources reference a processor by class-string (processor: YourProcessor::class). The built-in Eloquent processors are bound in the container by class name —ApiPlatform\Laravel\Eloquent\State\PersistProcessor(handles create/update, including BelongsTo/HasMany relations andstandard_put) and…\State\RemoveProcessor. To decorate, type-hint that concrete class in your constructor instead of using a#[Autowire(service: 'api_platform.doctrine.*')]attribute. Side-effect patterns (mailers, events) work the same via Laravel's container. Returning a SymfonyResponsefrom a processor is also supported.
Creating Related Entities in a Processor
Create child resources alongside the main resource:
final class AccountCreateProcessor implements ProcessorInterface
{
public function __construct(
#[Autowire(service: 'api_platform.doctrine_mongodb.odm.state.persist_processor')]
private ProcessorInterface $persistProcessor,
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
$this->hashPassword($data);
// Create related entities before persisting
foreach (['INBOX', 'Sent', 'Trash', 'Drafts'] as $name) {
$mailbox = new Mailbox();
$mailbox->path = $name;
$data->addMailbox($mailbox); // cascade: ['persist']
}
// Single persist call saves parent + children
return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
}
}
Soft-Delete Processor
Reusable processor for soft-deleting resources:
/**
* @implements ProcessorInterface<Account|Domain, Account|Domain>
*/
readonly class SoftDeleteProcessor implements ProcessorInterface
{
public function __construct(private DocumentManager $dm) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
$data->isDeleted = true;
$this->dm->persist($data);
$this->dm->flush();
return $data;
}
}
Assign to Delete operations:
new Delete(processor: SoftDeleteProcessor::class)
Combine with a collection extension that filters isDeleted = false to hide soft-deleted records from queries (see securing-collections skill).
Returning HTTP Responses (File Downloads)
Processors can return a Symfony Response for binary content:
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\Response;
/**
* @implements ProcessorInterface<Message, Response>
*/
final class DownloadProcessor implements ProcessorInterface
{
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Response
{
$content = $this->buildContent($data);
$response = new Response($content);
$response->headers->set('Content-Type', 'application/octet-stream');
$response->headers->set('Content-Disposition',
HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_ATTACHMENT, $data->getId() . '.bin')
);
return $response;
}
}
Use with a Get operation that has write: true:
new Get(
uriTemplate: '/orders/{id}/download',
write: true,
processor: DownloadProcessor::class,
name: '_api_order_download',
)
Handling Both Persist and Delete
When a single processor handles Create/Update AND Delete, inject both services:
public function __construct(
#[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
private ProcessorInterface $persistProcessor,
#[Autowire(service: 'api_platform.doctrine.orm.state.remove_processor')]
private ProcessorInterface $removeProcessor,
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
if ($operation instanceof DeleteOperationInterface) {
return $this->removeProcessor->process($data, $operation, $uriVariables, $context);
}
return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
}
Processor Parameters
- $data: The deserialized/validated object
- $operation: Operation metadata (Post, Patch, Delete, etc.)
- $uriVariables: URI path variables
- $context: Includes
previous_data(before modification),request,resource_class
Best Practices
- Always return data from processors — omitting breaks serialization
- Decorate built-in processors rather than replacing them
- Keep processors focused: one processor per operation when logic differs significantly
- Use
$context['previous_data']in Patch/Put to detect what changed - Throw
HttpExceptionsubclasses for domain errors (BadRequestHttpException,UnprocessableEntityHttpException)
Fichiers de cette skill
1Questions sur la skill state-processor
Comment installer la skill state-processor dans Claude Code ?
Exécutez npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processor. Claude Code charge les skills depuis ~/.claude/skills (tous les projets) ou .claude/skills (un seul projet) et utilise une skill lorsque sa description correspond à la tâche.
Quels clients peuvent utiliser la skill state-processor ?
Tout client qui lit le format Agent Skills : Claude Code, Codex, Gemini CLI et claude.ai (envoi sous forme de ZIP). Les scripts de la skill ne s'exécutent que si le client autorise l'exécution de code.
Quand un processeur d'état s'exécute-t-il ?
Un processeur s'exécute lorsque le flag 'write' de l'opération est défini sur true, généralement lors des opérations POST, PUT, PATCH ou DELETE.
Un processeur peut-il être utilisé pour des opérations GET ?
Oui, mais vous devez explicitement définir 'write: true' sur l'opération GET, ce qui est utile pour des tâches comme le téléchargement de fichiers ou la génération de rapports.
Comment ajouter des effets de bord comme l'envoi d'e-mails lors d'une opération d'écriture ?
Vous pouvez décorer le processeur de persistance intégré pour exécuter une logique avant ou après la sauvegarde des données.
Quelle est la différence entre un provider et un processor dans API Platform ?
Un state-provider gère le côté Query (lecture), tandis qu'un state-processor gère le côté Command (écriture) de la séparation de type CQRS.