From 00496e6fda3346d52c198ff29a7bfa4d3ea26162 Mon Sep 17 00:00:00 2001 From: Vincent Amstoutz Date: Mon, 30 Sep 2024 14:06:21 +0200 Subject: [PATCH] [WIP] Add Laravel support to state providers documentation This commit enhances the state providers documentation by including support for Laravel alongside Symfony. It details how to create custom state providers, configure them, and integrate with Laravel-specific ORM like Eloquent and MongoDB, ensuring parity with Symfony's state provider features. --- core/state-providers.md | 227 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 216 insertions(+), 11 deletions(-) diff --git a/core/state-providers.md b/core/state-providers.md index b5de9769c54..bb5f04f6c08 100644 --- a/core/state-providers.md +++ b/core/state-providers.md @@ -1,11 +1,19 @@ # State Providers -To retrieve data exposed by the API, API Platform uses classes called **state providers**. A state provider using [Doctrine -ORM](https://www.doctrine-project.org/projects/orm.html) to retrieve data from a database, a state provider using +To retrieve data exposed by the API, API Platform uses classes called **state providers**. + +With the Symfony variant, a state provider using [Doctrine +ORM](https://www.doctrine-project.org/projects/orm.html) is ready to retrieve data from a database and a state provider using [Doctrine MongoDB ODM](https://www.doctrine-project.org/projects/mongodb-odm.html) to retrieve data from a document -database, and a state provider using [Elasticsearch-PHP](https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html) -to retrieve data from an Elasticsearch cluster are included with the library. The first one is enabled by default. These -state providers natively support paged collections and filters. They can be used as-is and are perfectly suited to common uses. +database. + +With the Laravel variant, a state provider using [Eloquent ORM](https://laravel.com/docs/eloquent) to retrieve data from a relational database and a state provider using [Laravel MongoDB](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/) to retrieve data from a document database. + +The ORM providers are enabled by default, based on your framework variant (Eloquent or Doctrine will be set up). + +Also, both Symfony and Laravel variant come with a state provider to retrieve data from an Elasticsearch cluster using the library [Elasticsearch-PHP](https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html). + +These state providers natively support paged collections and filters. They can be used as-is and are perfectly suited to common uses. However, you sometimes want to retrieve data from other sources such as another persistence layer or a webservice. Custom state providers can be used to do so. A project can include as many state providers as needed. The first able to @@ -13,12 +21,17 @@ retrieve data for a given resource will be used. To do so you need to implement the `ApiPlatform\State\ProviderInterface`. -In the following examples we will create custom state providers for an entity class called `App\Entity\BlogPost`. -Note, that if your entity is not Doctrine-related, you need to flag the identifier property by using +In the following examples we will create custom state providers for Symfony entities and Laravel models: +- For Symfony we will create an entity class called `App\Entity\BlogPost`. +- For Laravel, we will create a model class called `App\Models\BlogPost`. + +Note, that if your entity is not Doctrine-related or Eloquent-related, you need to flag the identifier property by using `#[ApiProperty(identifier: true)` for things to work properly (see also [Entity Identifier Case](serialization.md#entity-identifier-case)). ## Creating a Custom State Provider +### Custom State Provider 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 provider easily: @@ -114,7 +127,7 @@ final class BlogPostProvider implements ProviderInterface } ``` -We then need to configure this same provider on the BlogPost `GetCollection` operation, or for every operations via the `ApiResource` attribute: +We then need to configure this same provider on the BlogPost `GetCollection` operation, or for every operation via the `ApiResource` attribute: ```php + */ +final class BlogPostProvider implements ProviderInterface +{ + private const DATA = [ + 'ab' => new BlogPost('ab'), + 'cd' => new BlogPost('cd'), + ]; + + public function provide(Operation $operation, array $uriVariables = [], array $context = []): BlogPost|null + { + return self::DATA[$uriVariables['id']] ?? null; + } +} +``` + +For the example, we store the list of our blog posts in an associative array (the `BlogPostProvider::DATA` constant). + +As this operation expects a `BlogPost`, the `provide` methods return the instance of the `BlogPost` corresponding to the ID passed in the URL. If the ID doesn't exist in the associative array, `provide()` returns `null`. API Platform will automatically generate a 404 response if the provider returns `null`. + +The `$uriVariables` parameter contains an array with the values of the URI variables. + +To use this provider we need to configure the provider on the operation: + +```php + + */ +final class BlogPostProvider implements ProviderInterface +{ + private const DATA = [ + 'ab' => new BlogPost('ab'), + 'cd' => new BlogPost('cd'), + ]; + + public function provide(Operation $operation, array $uriVariables = [], array $context = []): iterable|BlogPost|null + { + if ($operation instanceof CollectionOperationInterface) { + return self::DATA; + } + + return self::DATA[$uriVariables['id']] ?? null; + } +} +``` + +We then need to configure this same provider on the BlogPost `GetCollection` operation, or for every operation via the `ApiResource` attribute: + +```php +app->tag([BookRepresentationProvider::class], ProviderInterface::class); + } + + public function boot(): void + { + } +} +``` + +After that, you can inject the `ProviderInterface` +```php + + */ +final class BookRepresentationProvider implements ProviderInterface +{ + public function __construct( + private ProviderInterface $itemProvider, + ) + { + } + + public function provide(Operation $operation, array $uriVariables = [], array $context = []): AnotherRepresentation + { + $book = $this->itemProvider->provide($operation, $uriVariables, $context); + + return new AnotherRepresentation( + // Add DTO constructor params here. + // $book->getTitle(), + ); + } +} +``` + +And configure that you want to use this provider on the Book resource: + +```php +