From fc003d2e6ae46e8b17df7c31fcc3e21a066b39e0 Mon Sep 17 00:00:00 2001 From: Vincent Amstoutz Date: Mon, 30 Sep 2024 15:07:25 +0200 Subject: [PATCH 1/3] [WIP] Add Laravel support to state processors documentation This commit enhances the state processors documentation by including support for Laravel alongside Symfony. It details how to create custom state processors, configure them, and integrate with Laravel-specific ORM like Eloquent and MongoDB, ensuring parity with Symfony's state provider features. --- core/state-processors.md | 159 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 2 deletions(-) diff --git a/core/state-processors.md b/core/state-processors.md index e1aec6d02dc..f4076aa5e70 100644 --- a/core/state-processors.md +++ b/core/state-processors.md @@ -5,10 +5,14 @@ classes called **state processors**. State processors receive an instance of the the `#[ApiResource]` attribute). This instance contains data submitted by the client during [the deserialization process](serialization.md). -A state processor using [Doctrine ORM](https://www.doctrine-project.org/projects/orm.html) is included with the library and +With the Symfony variant, a state processor using [Doctrine ORM](https://www.doctrine-project.org/projects/orm.html) is included with the library and is enabled by default. It is able to persist and delete objects that are also mapped as [Doctrine entities](https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/basic-mapping.html). A [Doctrine MongoDB ODM](https://www.doctrine-project.org/projects/mongodb-odm.html) state processor is also included and can be enabled by following the [MongoDB documentation](mongodb.md). +With the Laravel variant, a state processor using [Eloquent ORM](https://laravel.com/docs/eloquent) is included with the library and +is enabled by default. It is able to persist and delete objects that are also mapped as [Related Models](https://laravel.com/docs/eloquent-relationships#inserting-and-updating-related-models). +A [Laravel MongoDB](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/) state processor is also included and can be enabled by following the [MongoDB documentation](../core/mongodb.md). + However, you may want to: * store data to other persistence layers (Elasticsearch, external web services...) @@ -20,6 +24,7 @@ process the data for a given resource will be used. ## Creating a Custom State Processor +### Custom State Processor with Symfony If the [Symfony MakerBundle](https://symfony.com/doc/current/bundles/SymfonyMakerBundle) is installed in your project, you can use the following command to generate a custom state processor easily: ```console @@ -75,8 +80,64 @@ use App\State\BlogPostProcessor; class BlogPost {} ``` +### Custom State Processor with Laravel +Using [Laravel Artisan Console](https://laravel.com/docs/artisan), you can generate a custom state processor easily with the following command: +```console +php artisan make:state-processor +``` + +To create a state processor, you have to implement the [`ProcessorInterface`](https://github.com/api-platform/core/blob/main/src/State/ProcessorInterface.php). +This interface defines a method `process`: to create, delete, update, or alter the given data in any ways. + +Here is an implementation example: + +```php + + */ +final class BlogPostProcessor implements ProcessorInterface +{ + /** + * @return BlogPost|void + */ + public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed + { + // call your persistence layer to save $data + return $data; + } +} +``` + +The `process()` method must return the created or modified object, or nothing (that's why `void` is allowed) for `DELETE` operations. +The `process()` method can also take an object as input, in the `$data` parameter, that isn't of the same type that its output (the returned object). See [the DTO documentation entry](dto.md) for more details. + +We then configure our operation to use this processor: + +```php + + */ +final class UserProcessor implements ProcessorInterface +{ + public function __construct( + private ProcessorInterface $persistProcessor, + private ProcessorInterface $removeProcessor, + ) + { + } + + /** + * @return User|void + */ + public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed + { + if ($operation instanceof DeleteOperationInterface) { + return $this->removeProcessor->process($data, $operation, $uriVariables, $context); + } + + $result = $this->persistProcessor->process($data, $operation, $uriVariables, $context); + $this->sendWelcomeEmail($data); + + return $result; + } + + private function sendWelcomeEmail(User $user): void + { + // Your welcome email logic... + // Mail::to($user->getEmail())->send(new WelcomeMail($user)); + } +} +``` + +Don't forget to tag the service with the `PersistProcessor` and the `RemoveProcessor` state classes. + +```php +app->tag([UserProcessor::class], [PersistProcessor::class, RemoveProcessor::class,]); + } + + public function boot(): void + { + } +} +``` +If you're using Laravel MongoDB ODM instead of Eloquent ORM, make sure you're using the right services. + +Finally, configure that you want to use this processor on the User resource: + +```php + Date: Fri, 11 Oct 2024 13:51:28 +0200 Subject: [PATCH 2/3] Update core/state-processors.md --- core/state-processors.md | 1 - 1 file changed, 1 deletion(-) diff --git a/core/state-processors.md b/core/state-processors.md index f4076aa5e70..45285bed6f8 100644 --- a/core/state-processors.md +++ b/core/state-processors.md @@ -11,7 +11,6 @@ A [Doctrine MongoDB ODM](https://www.doctrine-project.org/projects/mongodb-odm.h With the Laravel variant, a state processor using [Eloquent ORM](https://laravel.com/docs/eloquent) is included with the library and is enabled by default. It is able to persist and delete objects that are also mapped as [Related Models](https://laravel.com/docs/eloquent-relationships#inserting-and-updating-related-models). -A [Laravel MongoDB](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/) state processor is also included and can be enabled by following the [MongoDB documentation](../core/mongodb.md). However, you may want to: From 29bd0e3beda6fda7fcb8a7242a163655e966e3aa Mon Sep 17 00:00:00 2001 From: Antoine Bluchet Date: Fri, 11 Oct 2024 13:54:59 +0200 Subject: [PATCH 3/3] Apply suggestions from code review --- core/state-processors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/state-processors.md b/core/state-processors.md index 45285bed6f8..a8e69c25051 100644 --- a/core/state-processors.md +++ b/core/state-processors.md @@ -136,7 +136,7 @@ class BlogPost {} ## Hooking into the Built-In State Processors -### Symfony State Provider mechanism +### Symfony State Processor mechanism If you want to execute custom business logic before or after persistence, this can be achieved by using [composition](https://en.wikipedia.org/wiki/Object_composition). Here is an implementation example which uses [Symfony Mailer](https://symfony.com/doc/current/mailer.html) to send new users a welcome email after a REST `POST` or GraphQL `create` operation, in a project using the native Doctrine ORM state processor: @@ -211,7 +211,7 @@ use App\State\UserProcessor; class User {} ``` -### Laravel State Provider mechanism +### Laravel State Processor mechanism If you want to execute custom business logic before or after persistence, this can be achieved by using [composition](https://en.wikipedia.org/wiki/Object_composition). Here is an implementation example which uses [Laravel Mail](https://laravel.com/docs/mail) to send new users a welcome email after a REST `POST` or GraphQL `create` operation, in a project using the native Eloquent ORM state processor: