Skill state-processor
Membuat state processor untuk menangani persistensi dan side effects pada operasi write seperti POST, PUT, PATCH, dan DELETE.
- Bintang GitHub
- 29
- File
- 1
- Commit terakhir
- 22 Okt 2026
- Lisensi
- —
- Tool yang diizinkan
- —
Apa fungsi skill state-processor?
state-processor adalah agent skill. Membuat state processor untuk menangani persistensi dan side effects pada operasi write seperti POST, PUT, PATCH, dan DELETE.
Ringkasan dibuat dari SKILL.md skill ini.
Tentang skill state-processor
Skill ini memungkinkan Anda untuk mengelola operasi write dan side effects di API Platform. Ini memungkinkan developer untuk membuat state processor yang menangani sisi Command dari operasi bergaya CQRS, khususnya untuk request POST, PUT, PATCH, dan DELETE. Ketika sebuah agent perlu mengimplementasikan logika persistensi kustom, seperti hashing password, membuat entitas terkait, atau memicu notifikasi email, agent tersebut dapat menggunakan skill ini untuk menentukan bagaimana data diproses selama fase write.
Developer dapat menggunakan processor ini untuk mendekorasi service bawaan seperti Doctrine ORM persist processor atau MongoDB ODM remove processor. Pendekatan ini memungkinkan penambahan logika sebelum atau sesudah siklus persistensi standar. Skill ini juga mendukung use case tingkat lanjut seperti mengimplementasikan fungsionalitas soft-delete atau mengembalikan objek Symfony Response untuk download file dengan mengatur flag write ke true pada operasi GET. Skill ini bekerja di berbagai framework, termasuk Symfony dan Laravel Eloquent, dengan memanfaatkan ProcessorInterface.
Apa yang bisa dilakukan agent dengannya
- 1Implementasi custom side effectsTambahkan logika untuk mengirim notifikasi email segera setelah sebuah order berhasil di-persist.
- 2Menangani logika soft-deleteBuat processor yang dapat digunakan kembali untuk menandai sebuah resource sebagai terhapus alih-alih menghapusnya dari database.
- 3Generate download fileKonfigurasikan operasi GET untuk memicu processor yang mengembalikan response file biner.
- 4Otomatisasi pembuatan entitas terkaitPastikan resource anak seperti mailbox dibuat secara otomatis setiap kali ada akun baru yang terdaftar.
Ditulis dari SKILL.md milik skill tersebut. Setiap use case diverifikasi dengan kutipan darinya.
Cara install skill state-processor
Setiap perintah menyalin folder skill ke direktori skills client. Restart client atau mulai sesi baru untuk memuatnya.Install skill state-processor di Plugin
/plugin marketplace add api-platform/skillset /plugin install api-platform@api-platform-skillset
Install skill state-processor di Claude Code
~/.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processorInstall skill state-processor di Claude Code (project)
.claude/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor .claude/skills/state-processorInstall skill state-processor di Codex
~/.agents/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.agents/skills/state-processorInstall skill state-processor di Gemini CLI
~/.gemini/skills/state-processor/SKILL.md
$ npx degit api-platform/skillset/skills/state-processor ~/.gemini/skills/state-processorInstall skill state-processor di 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
Lihat di 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)
Berkas dalam skill ini
1Pertanyaan seputar skill state-processor
Bagaimana cara install skill state-processor di Claude Code?
Jalankan npx degit api-platform/skillset/skills/state-processor ~/.claude/skills/state-processor. Claude Code memuat skill dari ~/.claude/skills (semua proyek) atau .claude/skills (satu proyek) dan menggunakan skill saat deskripsinya cocok dengan tugas.
Client mana saja yang bisa menggunakan skill state-processor?
Client apa pun yang membaca format Agent Skills: Claude Code, Codex, Gemini CLI, dan claude.ai (unggah sebagai ZIP). Script dalam skill hanya berjalan di client yang mengizinkan eksekusi kode.
Kapan state processor dijalankan?
Processor dijalankan ketika flag 'write' pada operasi diatur ke true, biasanya selama operasi POST, PUT, PATCH, atau DELETE.
Apakah processor bisa digunakan untuk operasi GET?
Bisa, tetapi Anda harus mengatur 'write: true' secara eksplisit pada operasi GET, yang berguna untuk tugas seperti download file atau pembuatan laporan.
Bagaimana cara menambahkan side effects seperti mengirim email saat operasi write berlangsung?
Anda dapat melakukan dekorasi pada built-in persistence processor untuk mengeksekusi logika sebelum atau sesudah data disimpan.
Apa perbedaan antara provider dan processor di API Platform?
State-provider menangani sisi Query (read), sedangkan state-processor menangani sisi Command (write) dari pemisahan gaya CQRS.