Skip to content

Commit

Permalink
Add note on api platform state management
Browse files Browse the repository at this point in the history
  • Loading branch information
jslmorrison committed Nov 22, 2023
1 parent 348070a commit 62fdc8d
Show file tree
Hide file tree
Showing 2 changed files with 253 additions and 0 deletions.
122 changes: 122 additions & 0 deletions content-org/api-platform-state-management.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#+hugo_base_dir: ~/development/web/jslmorrison.github.io
#+hugo_section: posts
#+options: author:nil

* State management in Api Platform :symfony:api_platform:
:PROPERTIES:
:EXPORT_FILE_NAME: api-platform-state-management
:EXPORT_DATE: 2023-11-21
:END:
Fetch data and mutate state for custom api resources when using the [[https://api-platform.com/][Api Platform framework]].

#+hugo: more
** Define an api resource
The following defines an api resource for a product. Note the provider classes set for the operations annotation.
#+begin_src php :noeval
<?php

declare(strict_types=1);

namespace App\ApiResource;

use App\Entity\Product;
use ApiPlatform\Metadata\Get;
use App\State\ProductProvider;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;

#[ApiResource(
shortName: 'Product',
operations: [
new Get(provider: ProductProvider::class),
new GetCollection(provider: ProductProvider::class)
]
)]
final class ProductResource
{
public string $id;
public string $name;
public int $price;

public static function fromEntity(Product $entity): self
{
$productResource = new self();
$productResource->id = (string) $entity->id();
$productResource->name = $entity->name();
$productResource->price = $entity->price();

return $productResource;
}
}
#+end_src

** State Providers
The following defines a state provider for the above resource.
#+begin_src php :noeval
<?php

declare(strict_types=1);

namespace App\State;

use ApiPlatform\Metadata\Operation;
use App\ApiResource\ProductResource;
use App\Repository\ProductRepository;
use ApiPlatform\State\ProviderInterface;
use ApiPlatform\Metadata\CollectionOperationInterface;
use Symfony\Component\Uid\Ulid;

final class ProductProvider implements ProviderInterface
{
public function __construct(private readonly ProductRepository $productRepository)
{
}

public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
if ($operation instanceof CollectionOperationInterface) {
$products = [];

foreach ($this->productRepository->findAll() as $product) {
$products[] = ProductResource::fromEntity($product);
}

return $products;
}

return ProductResource::fromEntity($this->productRepository->find(Ulid::fromString($uriVariables['id'])));
}
}
#+end_src

** State Processors
Continuing with the above resource, in order to mutate its state a new operation should be configured. E.g to enable post request to create new product:
#+begin_src php :noeval
#[ApiResource(
shortName: 'Product',
operations: [
new Get(provider: ProductProvider::class),
new GetCollection(provider: ProductProvider::class),
new Post(processor: ProductProcessor::class), // new operation defining processor
]
)]
final class ProductResource
#+end_src

And the corresponding processor:
#+begin_src php :noeval
// App\State\ProductProcessor.php

final class ProductProcessor implements ProcessorInterface
{
public function process($data, Operation $operation, array $uriVariables = [], array $context = [])
{
// mutate and persist data as you like here, e.g:
$this->productRepository->add(ProductResource::toEntity($data));

return $data;
}
}
#+end_src

Read the documentation for more info on [[https://api-platform.com/docs/v3.2/core/state-providers/][state providers]] and [[https://api-platform.com/docs/v3.2/core/state-processors/][state processors]].
131 changes: 131 additions & 0 deletions content/posts/api-platform-state-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
+++
title = "State management in Api Platform"
date = 2023-11-21
tags = ["symfony", "api-platform"]
draft = false
+++

Fetch data and mutate state for custom api resources when using the [Api Platform framework](https://api-platform.com/).

<!--more-->


## Define an api resource {#define-an-api-resource}

The following defines an api resource for a product. Note the provider classes set for the operations annotation.

```php
<?php

declare(strict_types=1);

namespace App\ApiResource;

use App\Entity\Product;
use ApiPlatform\Metadata\Get;
use App\State\ProductProvider;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;

#[ApiResource(
shortName: 'Product',
operations: [
new Get(provider: ProductProvider::class),
new GetCollection(provider: ProductProvider::class)
]
)]
final class ProductResource
{
public string $id;
public string $name;
public int $price;

public static function fromEntity(Product $entity): self
{
$productResource = new self();
$productResource->id = (string) $entity->id();
$productResource->name = $entity->name();
$productResource->price = $entity->price();

return $productResource;
}
}
```


## State Providers {#state-providers}

The following defines a state provider for the above resource.

```php
<?php

declare(strict_types=1);

namespace App\State;

use ApiPlatform\Metadata\Operation;
use App\ApiResource\ProductResource;
use App\Repository\ProductRepository;
use ApiPlatform\State\ProviderInterface;
use ApiPlatform\Metadata\CollectionOperationInterface;
use Symfony\Component\Uid\Ulid;

final class ProductProvider implements ProviderInterface
{
public function __construct(private readonly ProductRepository $productRepository)
{
}

public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
if ($operation instanceof CollectionOperationInterface) {
$products = [];

foreach ($this->productRepository->findAll() as $product) {
$products[] = ProductResource::fromEntity($product);
}

return $products;
}

return ProductResource::fromEntity($this->productRepository->find(Ulid::fromString($uriVariables['id'])));
}
}
```


## State Processors {#state-processors}

Continuing with the above resource, in order to mutate its state a new operation should be configured. E.g to enable post request to create new product:

```php
#[ApiResource(
shortName: 'Product',
operations: [
new Get(provider: ProductProvider::class),
new GetCollection(provider: ProductProvider::class),
new Post(processor: ProductProcessor::class), // new operation defining processor
]
)]
final class ProductResource
```

And the corresponding processor:

```php
// App\State\ProductProcessor.php

final class ProductProcessor implements ProcessorInterface
{
public function process($data, Operation $operation, array $uriVariables = [], array $context = [])
{
// mutate and persist data as you like here, e.g:
$this->productRepository->add(ProductResource::toEntity($data));

return $data;
}
}
```

Read the documentation for more info on [state providers](https://api-platform.com/docs/v3.2/core/state-providers/) and [state processors](https://api-platform.com/docs/v3.2/core/state-processors/).

0 comments on commit 62fdc8d

Please sign in to comment.