Skill state-processor
Cria state processors para lidar com persistência e efeitos colaterais em operações de escrita como POST, PUT, PATCH e DELETE.
- Estrelas no GitHub
- 29
- Arquivos
- 1
- Último commit
- 22 de out. de 2026
- Licença
- —
- Ferramentas permitidas
- —
O que a skill state-processor faz?
state-processor é uma Agent Skill. Cria state processors para lidar com persistência e efeitos colaterais em operações de escrita como POST, PUT, PATCH e DELETE.
Resumo gerado a partir do SKILL.md da skill.
Sobre a skill state-processor
Gerenciar operações de escrita e efeitos colaterais no API Platform é o que esta skill permite. Ela permite que desenvolvedores criem state processors que lidam com o lado de Command de operações no estilo CQRS, especificamente para requisições POST, PUT, PATCH e DELETE. Quando um agente precisa implementar uma lógica de persistência customizada, como fazer o hash de senhas, criar entidades relacionadas ou disparar notificações por e-mail, ele pode usar esta skill para definir como os dados são processados durante a fase de escrita.
Desenvolvedores podem usar esses processors para decorar serviços nativos, como o Doctrine ORM persist processor ou o MongoDB ODM remove processor. Essa abordagem permite adicionar lógica antes ou depois do ciclo de persistência padrão. A skill também suporta casos de uso avançados, como implementar funcionalidade de soft-delete ou retornar objetos Symfony Response para downloads de arquivos, definindo a flag write como true em operações GET. Ela funciona em diferentes frameworks, incluindo Symfony e Laravel Eloquent, utilizando o ProcessorInterface.
O que um agente pode fazer com ela
- 1Implementar efeitos colaterais customizadosAdicione lógica para enviar uma notificação por e-mail imediatamente após um pedido ser persistido com sucesso.
- 2Lidar com lógica de soft-deleteCrie um processor reutilizável que marca um recurso como deletado em vez de removê-lo do banco de dados.
- 3Gerar downloads de arquivosConfigure uma operação GET para disparar um processor que retorna uma resposta de arquivo binário.
- 4Automatizar a criação de entidades relacionadasGaranta que recursos filhos, como caixas de e-mail, sejam criados automaticamente sempre que uma nova conta for registrada.
Escrito a partir do SKILL.md da skill. Cada caso de uso é verificado com uma citação dele.
Como instalar a skill state-processor
Cada comando copia a pasta da skill para o diretório de skills do cliente. Reinicie o cliente ou inicie uma nova sessão para carregá-la.Instalar a skill state-processor no Plugin
/plugin marketplace add api-platform/skillset /plugin install api-platform@api-platform-skillset
Instalar a skill state-processor no Claude Code
~/.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processorInstalar a skill state-processor no Claude Code (project)
.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor .claude/skills/state-processorInstalar a skill state-processor no Codex
~/.agents/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.agents/skills/state-processorInstalar a skill state-processor no Gemini CLI
~/.gemini/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.gemini/skills/state-processorInstalar a skill state-processor no 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
Ver no 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)
Arquivos desta skill
1Perguntas sobre a skill state-processor
Como instalo a skill state-processor no Claude Code?
Execute npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processor. O Claude Code carrega skills de ~/.claude/skills (todos os projetos) ou .claude/skills (um projeto) e usa uma skill quando a descrição dela corresponde à tarefa.
Quais clientes podem usar a skill state-processor?
Qualquer cliente que leia o formato Agent Skills: Claude Code, Codex, Gemini CLI e claude.ai (envie como ZIP). Scripts da skill são executados apenas onde o cliente permite execução de código.
Quando um state processor é executado?
Um processor é executado quando a flag 'write' da operação está definida como true, tipicamente durante operações POST, PUT, PATCH ou DELETE.
Um processor pode ser usado em operações GET?
Sim, mas você deve definir explicitamente 'write: true' na operação GET, o que é útil para tarefas como download de arquivos ou geração de relatórios.
Como adiciono efeitos colaterais, como o envio de e-mails, durante uma operação de escrita?
Você pode decorar o processor de persistência nativo para executar lógica antes ou depois de os dados serem salvos.
Qual é a diferença entre um provider e um processor no API Platform?
Um state-provider cuida do lado de Query (leitura), enquanto um state-processor cuida do lado de Command (escrita) da divisão no estilo CQRS.