Навык state-processor
Создает процессоры состояния для обработки персистентности и побочных эффектов при операциях записи, таких как POST, PUT, PATCH и DELETE.
- Звёзды на GitHub
- 29
- Файлы
- 1
- Последний коммит
- 22 окт. 2026 г.
- Лицензия
- —
- Разрешённые инструменты
- —
Что делает навык state-processor?
state-processor — это Agent Skill. Создает процессоры состояния для обработки персистентности и побочных эффектов при операциях записи, таких как POST, PUT, PATCH и DELETE.
Сводка сгенерирована из SKILL.md навыка.
О skill state-processor
Этот SKILL позволяет управлять операциями записи и побочными эффектами в API Platform. Он дает разработчикам возможность создавать процессоры состояния, которые обрабатывают сторону Command в операциях по типу CQRS, в частности для запросов POST, PUT, PATCH и DELETE. Когда агенту необходимо реализовать кастомную логику сохранения данных, например хеширование паролей, создание связанных сущностей или запуск уведомлений по электронной почте, он может использовать этот SKILL для определения процесса обработки данных на этапе записи.
Разработчики могут использовать эти процессоры для декорирования встроенных сервисов, таких как процессор сохранения Doctrine ORM или процессор удаления MongoDB ODM. Такой подход позволяет добавлять логику до или после стандартного цикла сохранения. SKILL также поддерживает продвинутые сценарии использования, такие как реализация функционала soft-delete или возврат объектов Symfony Response для скачивания файлов путем установки флага write в значение true для GET-операций. Он работает в различных фреймворках, включая Symfony и Laravel Eloquent, используя ProcessorInterface.
Что агент может делать с ним
- 1Реализация кастомных побочных эффектовДобавление логики для отправки уведомления по электронной почте сразу после успешного сохранения заказа.
- 2Обработка логики soft-deleteСоздание переиспользуемого процессора, который помечает ресурс как удаленный вместо его физического удаления из базы данных.
- 3Генерация скачиваемых файловНастройка GET-операции для запуска процессора, который возвращает бинарный файл в ответе.
- 4Автоматизация создания связанных сущностейОбеспечение автоматического создания дочерних ресурсов, таких как почтовые ящики, при регистрации каждого нового аккаунта.
Написано на основе skill SKILL.md. Каждый сценарий использования проверен по цитате из него.
Как установить навык state-processor
Каждая команда копирует папку навыка в каталог навыков клиента. Перезапустите клиент или начните новую сессию, чтобы загрузить его.Установка навыка state-processor в Плагин
/plugin marketplace add api-platform/skillset /plugin install api-platform@api-platform-skillset
Установка навыка state-processor в Claude Code
~/.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processorУстановка навыка state-processor в Claude Code (project)
.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor .claude/skills/state-processorУстановка навыка state-processor в Codex
~/.agents/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.agents/skills/state-processorУстановка навыка state-processor в Gemini CLI
~/.gemini/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.gemini/skills/state-processorУстановка навыка state-processor в 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
Открыть на 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)
Файлы в этом навыке
1Вопросы о навыке state-processor
Как установить навык state-processor в Claude Code?
Выполните npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processor. Claude Code загружает навыки из ~/.claude/skills (все проекты) или .claude/skills (один проект) и использует навык, когда его описание соответствует задаче.
Какие клиенты могут использовать навык state-processor?
Любой клиент, который читает формат Agent Skills: Claude Code, Codex, Gemini CLI и claude.ai (загрузить как ZIP). Скрипты в навыке выполняются только там, где клиент разрешает выполнение кода.
Когда запускается процессор состояния?
Процессор запускается, когда флаг 'write' операции установлен в значение true, что обычно происходит во время операций POST, PUT, PATCH или DELETE.
Можно ли использовать процессор для GET-операций?
Да, но вы должны явно установить 'write: true' для GET-операции, что полезно для таких задач, как скачивание файлов или генерация отчетов.
Как добавить побочные эффекты, например отправку писем, во время операции записи?
Вы можете декорировать встроенный процессор персистентности, чтобы выполнить логику до или после сохранения данных.
В чем разница между провайдером и процессором в API Platform?
State-provider отвечает за сторону Query (чтение), а state-processor — за сторону Command (запись) в разделении по принципу CQRS.