Guides
Tutorials
Set the messenger
option to input
, and API Platform will automatically dispatch the given Input as a message instead of the Resource. Indeed, it'll add a default DataTransformer
(see input/output) that handles the given input
. In this example, we'll handle a ResetPasswordRequest
on a custom operation on our User resource:
// src/App/Entity.php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
use App\Dto\ImportBookRequest;
use Doctrine\ORM\Mapping as ORM;
#[ApiResource(
operations: [
new Post(
uriTemplate: '/books/import',
/*
* 202 Accepted HTTP Status indicates that the request has been received and will be treated later,
* without giving an immediate return to the client
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202
*/
status: 202,
/*
* Use a custom Input object for this route.
*/
input: ImportBookRequest::class,
/*
* The HTTP response that will be generated by API Platform will be empty,
* and the serialization process will be skipped.
*/
output: false,
/*
* Use `messenger=input` to use the Input object instead of the current one.
*/
messenger: 'input',
)
]
)]
#[ORM\Entity]
final class Book
{
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
public string $id;
#[ORM\Column]
public string $isbn;
#[ORM\Column]
public string $title;
#[ORM\Column]
public string $author;
}
// src/DoctrineMigrations.php
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Migration extends AbstractMigration
{
public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE book (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, isbn VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL, author VARCHAR(255) NOT NULL)');
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE book');
}
}
// src/App/Dto.php
namespace App\Dto;
use Symfony\Component\Validator\Constraints as Assert;
/*
* This Input object only aims to import a Book from its ISBN.
*/
final class ImportBookRequest
{
#[Assert\NotBlank]
public string $isbn;
}
// src/App/Handler.php
namespace App\Handler;
use App\Dto\ImportBookRequest;
use App\Entity\Book;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
#[AsMessageHandler]
final class ImportBookRequestHandler
{
public function __construct(private readonly EntityManagerInterface $entityManager)
{
}
/*
* When a `POST` request is issued on `/books/import`, this Message Handler will receive an
* `App\Dto\ImportBookRequest` object instead of a `Book` one because we specified it as `input`
* and set `messenger=input`.
*/
public function __invoke(ImportBookRequest $request)
{
/*
* Create the real Book object from the Input one.
* (you should probably want to import the Book data from a public API)
*/
$book = new Book();
$book->isbn = $request->isbn;
$book->title = 'Le problème à trois corps';
$book->author = 'Cixin Liu';
/*
* Save the real Book object in the database.
*/
$this->entityManager->persist($book);
$this->entityManager->flush();
}
}
// src/App/Playground.php
namespace App\Playground;
use Symfony\Component\HttpFoundation\Request;
function request(): Request
{
return Request::create('/books/import', 'POST', [
'headers' => [
'Content-Type' => 'application/json',
],
], [], [], [], [
'json' => <<<JSON
sbn": "9782330113551"
JSON
]);
}
}
// src/App/Tests.php
namespace App\Tests;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Entity\Book;
use Symfony\Component\HttpFoundation\Response;
use PhpDocumentGenerator\Playground\TestGuideTrait;
final class BookTest extends ApiTestCase
{
use TestGuideTrait;
public function testAsAnonymousICanAccessTheDocumentation(): void
{
/*
* Import Book using its ISBN.
*/
static::createClient()->request('POST', '/books/import', [
'json' => [
'isbn' => '9782330113551',
],
]);
$this->assertResponseStatusCodeSame(Response::HTTP_ACCEPTED);
/*
* Check the Message Handler has been successfully called.
* The Book should have been imported and created in the database.
*/
static::getContainer()->get('doctrine')->getRepository(Book::class)->findOneBy([
'isbn' => '9782330113551',
]);
}
}
// src/App/Entity.php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
use App\Dto\ImportBookRequest;
use Doctrine\ORM\Mapping as ORM;
#[ApiResource(
operations: [
new Post(
uriTemplate: '/books/import',
/*
* 202 Accepted HTTP Status indicates that the request has been received and will be treated later,
* without giving an immediate return to the client
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202
*/
status: 202,
/*
* Use a custom Input object for this route.
*/
input: ImportBookRequest::class,
/*
* The HTTP response that will be generated by API Platform will be empty,
* and the serialization process will be skipped.
*/
output: false,
/*
* Use `messenger=input` to use the Input object instead of the current one.
*/
messenger: 'input',
)
]
)]
#[ORM\Entity]
final class Book
{
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
public string $id;
#[ORM\Column]
public string $isbn;
#[ORM\Column]
public string $title;
#[ORM\Column]
public string $author;
}
// src/DoctrineMigrations.php
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Migration extends AbstractMigration
{
public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE book (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, isbn VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL, author VARCHAR(255) NOT NULL)');
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE book');
}
}
// src/App/Dto.php
namespace App\Dto;
use Symfony\Component\Validator\Constraints as Assert;
/*
* This Input object only aims to import a Book from its ISBN.
*/
final class ImportBookRequest
{
#[Assert\NotBlank]
public string $isbn;
}
// src/App/Handler.php
namespace App\Handler;
use App\Dto\ImportBookRequest;
use App\Entity\Book;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
#[AsMessageHandler]
final class ImportBookRequestHandler
{
public function __construct(private readonly EntityManagerInterface $entityManager)
{
}
/*
* When a `POST` request is issued on `/books/import`, this Message Handler will receive an
* `App\Dto\ImportBookRequest` object instead of a `Book` one because we specified it as `input`
* and set `messenger=input`.
*/
public function __invoke(ImportBookRequest $request)
{
/*
* Create the real Book object from the Input one.
* (you should probably want to import the Book data from a public API)
*/
$book = new Book();
$book->isbn = $request->isbn;
$book->title = 'Le problème à trois corps';
$book->author = 'Cixin Liu';
/*
* Save the real Book object in the database.
*/
$this->entityManager->persist($book);
$this->entityManager->flush();
}
}
// src/App/Playground.php
namespace App\Playground;
use Symfony\Component\HttpFoundation\Request;
function request(): Request
{
return Request::create('/books/import', 'POST', [
'headers' => [
'Content-Type' => 'application/json',
],
], [], [], [], [
'json' => <<<JSON
sbn": "9782330113551"
JSON
]);
}
}
// src/App/Tests.php
namespace App\Tests;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Entity\Book;
use Symfony\Component\HttpFoundation\Response;
use PhpDocumentGenerator\Playground\TestGuideTrait;
final class BookTest extends ApiTestCase
{
use TestGuideTrait;
public function testAsAnonymousICanAccessTheDocumentation(): void
{
/*
* Import Book using its ISBN.
*/
static::createClient()->request('POST', '/books/import', [
'json' => [
'isbn' => '9782330113551',
],
]);
$this->assertResponseStatusCodeSame(Response::HTTP_ACCEPTED);
/*
* Check the Message Handler has been successfully called.
* The Book should have been imported and created in the database.
*/
static::getContainer()->get('doctrine')->getRepository(Book::class)->findOneBy([
'isbn' => '9782330113551',
]);
}
}