To mutate the application states during POST
, PUT
, PATCH
or DELETE
operations, API Platform uses
classes called data persisters. Data persisters receive an instance of the class marked as an API resource (usually using
the #[ApiResource]
attribute). This instance contains data submitted by the client during the deserialization
process.
Watch the Data Persister screencast
A data persister using Doctrine ORM 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. A Doctrine MongoDB ODM data persister is also included and can be enabled by following the MongoDB documentation.
However, you may want to:
Custom data persisters can be used to do so. A project can include as many data persisters as needed. The first able to persist data for a given resource will be used.
To create a data persister, you have to implement the ContextAwareDataPersisterInterface
.
This interface defines only 3 methods:
persist
: to create or update the given dataremove
: to delete the given datasupports
: to check whether the given data is supported by this data persisterHere is an implementation example:
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\BlogPost;
final class BlogPostDataPersister implements ContextAwareDataPersisterInterface
{
public function supports($data, array $context = []): bool
{
return $data instanceof BlogPost;
}
public function persist($data, array $context = [])
{
// call your persistence layer to save $data
return $data;
}
public function remove($data, array $context = [])
{
// call your persistence layer to delete $data
}
}
If service autowiring and autoconfiguration are enabled (they are by default), you are done!
Otherwise, if you use a custom dependency injection configuration, you need to register the corresponding service and add the
api_platform.data_persister
tag. The priority
attribute can be used to order persisters.
# api/config/services.yaml
services:
# ...
App\DataPersister\BlogPostDataPersister: ~
# Uncomment only if autoconfiguration is disabled
#tags: [ 'api_platform.data_persister' ]
Note that if you don’t need any $context
in your data persister’s methods, you can implement the DataPersisterInterface
instead.
Watch the Data Persister Decoration screencast
If you want to execute custom business logic before or after persistence, this can be achieved by decorating the built-in data persisters.
The next example uses Symfony Mailer. Read its documentation if you want to use it.
Here is an implementation example which sends new users a welcome email after a REST POST
or GraphQL create
operation, in a project using the native Doctrine ORM data persister:
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\User;
use Symfony\Component\Mailer\MailerInterface;
final class UserDataPersister implements ContextAwareDataPersisterInterface
{
private $decorated;
private $mailer;
public function __construct(ContextAwareDataPersisterInterface $decorated, MailerInterface $mailer)
{
$this->decorated = $decorated;
$this->mailer = $mailer;
}
public function supports($data, array $context = []): bool
{
return $this->decorated->supports($data, $context);
}
public function persist($data, array $context = [])
{
$result = $this->decorated->persist($data, $context);
if (
$data instanceof User && (
($context['collection_operation_name'] ?? null) === 'post' ||
($context['graphql_operation_name'] ?? null) === 'create'
)
) {
$this->sendWelcomeEmail($data);
}
return $result;
}
public function remove($data, array $context = [])
{
return $this->decorated->remove($data, $context);
}
private function sendWelcomeEmail(User $user)
{
// Your welcome email logic...
// $this->mailer->send(...);
}
}
Even with service autowiring and autoconfiguration enabled, you must still configure the decoration:
# api/config/services.yaml
services:
# ...
App\DataPersister\UserDataPersister:
bind:
$decorated: '@api_platform.doctrine.orm.data_persister'
# Uncomment only if autoconfiguration is disabled
#arguments: ['@App\DataPersister\UserDataPersister.inner']
#tags: [ 'api_platform.data_persister' ]
Our DataPersisters are called in chain, once a data persister is supported the chain breaks and API Platform assumes your data is persisted. You can call multiple data persisters by implementing the ResumableDataPersisterInterface
:
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\BlogPost;
final class BlogPostDataPersister implements ContextAwareDataPersisterInterface, ResumableDataPersisterInterface
{
public function supports($data, array $context = []): bool
{
return $data instanceof BlogPost;
}
public function persist($data, array $context = [])
{
// call your persistence layer to save $data
return $data;
}
public function remove($data, array $context = [])
{
// call your persistence layer to delete $data
}
// Once called this data persister will resume to the next one
public function resumable(array $context = []): bool
{
return true;
}
}
This is useful when using Messenger
with API Platform as you may want to do something asynchronously with the data but still call the default Doctrine data persister, for example:
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\BlogPost;
final class BlogPostDataPersister implements ContextAwareDataPersisterInterface, ResumableDataPersisterInterface
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function supports($data, array $context = []): bool
{
return $data instanceof BlogPost;
}
public function persist($data, array $context = [])
{
$this->entityManager->persist($data);
$this->entityManager->flush();
}
public function remove($data, array $context = [])
{
$this->entityManager->remove($data);
$this->entityManager->flush();
}
// Once called this data persister will resume to the next one
public function resumable(array $context = []): bool
{
return true;
}
}
# api/config/services.yaml
services:
# ...
App\DataPersister\BlogPostDataPersister: ~
# Uncomment only if autoconfiguration is disabled
#arguments: ['@App\DataPersister\BlogPostDataPersister.inner']
#tags: [ 'api_platform.data_persister' ]
Made with love by
Les-Tilleuls.coop can help you design and develop your APIs and web projects, and train your teams in API Platform, Symfony, Next.js, Kubernetes and a wide range of other technologies.
Learn more