Skill state-processor
Crea procesadores de estado para gestionar la persistencia y los efectos secundarios en operaciones de escritura como POST, PUT, PATCH y DELETE.
- Estrellas en GitHub
- 29
- Archivos
- 1
- Último commit
- 22 oct 2026
- Licencia
- —
- Herramientas permitidas
- —
¿Qué hace la skill state-processor?
state-processor es una agent skill. Crea procesadores de estado para gestionar la persistencia y los efectos secundarios en operaciones de escritura como POST, PUT, PATCH y DELETE.
Resumen generado a partir del SKILL.md de la skill.
Sobre la skill state-processor
Esta SKILL permite gestionar las operaciones de escritura y los efectos secundarios en API Platform. Permite a los desarrolladores crear state processors que gestionan el lado de Command de las operaciones estilo CQRS, específicamente para peticiones POST, PUT, PATCH y DELETE. Cuando un agente necesita implementar lógica de persistencia personalizada, como el hashing de contraseñas, la creación de entidades relacionadas o el envío de notificaciones por correo electrónico, puede usar esta SKILL para definir cómo se procesan los datos durante la fase de escritura.
Los desarrolladores pueden usar estos procesadores para decorar servicios integrados como el Doctrine ORM persist processor o el MongoDB ODM remove processor. Este enfoque permite añadir lógica antes o después del ciclo de persistencia estándar. La SKILL también admite casos de uso avanzados, como la implementación de funcionalidad de soft-delete o la devolución de objetos Symfony Response para descargas de archivos al establecer la bandera write en true en las operaciones GET. Funciona en diferentes frameworks, incluidos Symfony y Laravel Eloquent, mediante el uso de ProcessorInterface.
Lo que un agente puede hacer con ella
- 1Implementar efectos secundarios personalizadosAñade lógica para enviar una notificación por correo electrónico inmediatamente después de que un pedido se haya persistido correctamente.
- 2Gestionar la lógica de soft-deleteCrea un procesador reutilizable que marque un recurso como eliminado en lugar de borrarlo de la base de datos.
- 3Generar descargas de archivosConfigura una operación GET para activar un procesador que devuelva una respuesta de archivo binario.
- 4Automatizar la creación de entidades relacionadasAsegura que los recursos hijos, como buzones de correo, se creen automáticamente cada vez que se registre una nueva cuenta.
Escrito a partir del SKILL.md de la skill. Cada caso de uso se verifica con una cita del mismo.
Cómo instalar la skill state-processor
Cada comando copia la carpeta de la skill al directorio de skills del cliente. Reinicia el cliente o inicia una nueva sesión para cargarla.Instalar la skill state-processor en Plugin
/plugin marketplace add api-platform/skillset /plugin install api-platform@api-platform-skillset
Instalar la skill state-processor en Claude Code
~/.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processorInstalar la skill state-processor en Claude Code (project)
.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor .claude/skills/state-processorInstalar la skill state-processor en Codex
~/.agents/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.agents/skills/state-processorInstalar la skill state-processor en Gemini CLI
~/.gemini/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.gemini/skills/state-processorInstalar la skill state-processor en 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 en 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)
Archivos de esta skill
1Preguntas sobre la skill state-processor
¿Cómo instalo la skill state-processor en Claude Code?
Ejecuta npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processor. Claude Code carga las skills desde ~/.claude/skills (todos los proyectos) o .claude/skills (un proyecto) y usa una skill cuando su descripción coincide con la tarea.
¿Qué clientes pueden usar la skill state-processor?
Cualquier cliente que lea el formato Agent Skills: Claude Code, Codex, Gemini CLI y claude.ai (subir como ZIP). Los scripts de la skill se ejecutan solo donde el cliente permite la ejecución de código.
¿Cuándo se ejecuta un procesador de estado?
Un procesador se ejecuta cuando la bandera 'write' de la operación está establecida en true, normalmente durante operaciones POST, PUT, PATCH o DELETE.
¿Se puede usar un procesador para operaciones GET?
Sí, pero debes establecer explícitamente 'write: true' en la operación GET, lo cual es útil para tareas como descargas de archivos o generación de informes.
¿Cómo añado efectos secundarios, como el envío de correos electrónicos, durante una operación de escritura?
Puedes decorar el procesador de persistencia integrado para ejecutar lógica antes o después de que se guarden los datos.
¿Cuál es la diferencia entre un provider y un processor en API Platform?
Un state-provider gestiona el lado de Query (lectura), mientras que un state-processor gestiona el lado de Command (escritura) de la división estilo CQRS.