In the previous chapter, we have created the Sylius resource. Now, we need to create the basic operations. To achieve that, we reuse commands & queries we already have in the Application folder to create providers & processors.
Copy src
└── BookStore
├── Application
│ ├── Command
│ │ ├── CreateBookCommand.php
│ │ ├── UpdateBookCommand.php
│ │ └── DeleteBookCommand.php
│ └── Query
│ └── FindBookQuery.php
├── Domain
└── Infrastructure
└── Sylius
└── State
├── Provider
│ └── BookItemProvider.php
└── Processor
├── CreateBookProcessor.php
├── UpdateBookProcessor.php
└── DeleteBookProcessor.php
Book creation
In the Application folder, we already have this CreateBookCommand
:
src/Bookstore/Application/Command/CreateBookCommand.php
Copy
declare (strict_types = 1 );
namespace App \ BookStore \ Application \ Command ;
use App \ BookStore \ Domain \ Model \ Book ;
use App \ BookStore \ Domain \ ValueObject \ Author ;
use App \ BookStore \ Domain \ ValueObject \ BookContent ;
use App \ BookStore \ Domain \ ValueObject \ BookDescription ;
use App \ BookStore \ Domain \ ValueObject \ BookName ;
use App \ BookStore \ Domain \ ValueObject \ Price ;
use App \ Shared \ Application \ Command \ CommandInterface ;
/**
* @implements CommandInterface<Book>
*/
final readonly class CreateBookCommand implements CommandInterface
{
public function __construct (
public BookName $name ,
public BookDescription $description ,
public Author $author ,
public BookContent $content ,
public Price $price ,
) {
}
}
The idea is to reuse this command to create the book in the storage for your "create" operation.
Create the CreateBookProcessor
First, we need to add the CreateBookProcessor
in which we're going to call our CreateBookCommand
.
src/BookStore/Infrastructure/Sylius/State/Processor/CreateBookProcessor.php
Copy
declare (strict_types = 1 );
namespace App \ BookStore \ Infrastructure \ Sylius \ State \ Processor ;
use App \ BookStore \ Application \ Command \ CreateBookCommand ;
use App \ BookStore \ Domain \ ValueObject \ Author ;
use App \ BookStore \ Domain \ ValueObject \ BookContent ;
use App \ BookStore \ Domain \ ValueObject \ BookDescription ;
use App \ BookStore \ Domain \ ValueObject \ BookName ;
use App \ BookStore \ Domain \ ValueObject \ Price ;
use App \ BookStore \ Infrastructure \ Sylius \ Resource \ BookResource ;
use App \ Shared \ Application \ Command \ CommandBusInterface ;
use Sylius \ Resource \ Context \ Context ;
use Sylius \ Resource \ Metadata \ Operation ;
use Sylius \ Resource \ State \ ProcessorInterface ;
use Webmozart \ Assert \ Assert ;
/**
* @implements ProcessorInterface<BookResource>
*/
final readonly class CreateBookProcessor implements ProcessorInterface
{
public function __construct (
private CommandBusInterface $commandBus ,
) {
}
public function process ( mixed $data , Operation $operation , Context $context) : BookResource
{
Assert :: isInstanceOf ( $data , BookResource ::class ) ;
Assert :: notNull ( $data -> name ) ;
Assert :: notNull ( $data -> description ) ;
Assert :: notNull ( $data -> author ) ;
Assert :: notNull ( $data -> content ) ;
Assert :: notNull ( $data -> price ) ;
$command = new CreateBookCommand (
new BookName ($data -> name) ,
new BookDescription ($data -> description) ,
new Author ($data -> author) ,
new BookContent ($data -> content) ,
new Price ($data -> price) ,
);
$model = $this -> commandBus -> dispatch ( $command ) ;
return BookResource :: fromModel ( $model ) ;
}
}
Adding the processor on the Book Resource
Then, we add the Create
operation on our BookResource
.
src/BookStore/Infrastructure/Sylius/Resource/BookResource.php
Copy
// ...
use App \ BookStore \ Infrastructure \ Sylius \ State \ Processor \ CreateBookProcessor ;
use App \ BookStore \ Infrastructure \ Symfony \ Form \ BookResourceType ;
use Sylius \ Resource \ Metadata \ Create ;
use Sylius \ Resource \ Model \ ResourceInterface ;
// ...
#[ AsResource (
// ...
formType : BookResourceType ::class , // Define the form type for all your operations
operations : [
new Create (
processor: CreateBookProcessor ::class , // the processor we have just created
// formType: CreateBookResourceType::class, // Optional: define a specific form type only for the "create" operation
) ,
] ,
) ]
final class BookResource implements ResourceInterface
{
// ...
}
Book edition
Now, we want to be able to edit an existing book. In the Application folder, we already have this UpdateBookCommand
:
src/Bookstore/Application/Command/UpdateBookCommand.php
Copy
declare (strict_types = 1 );
namespace App \ BookStore \ Application \ Command ;
use App \ BookStore \ Domain \ Model \ Book ;
use App \ BookStore \ Domain \ ValueObject \ Author ;
use App \ BookStore \ Domain \ ValueObject \ BookContent ;
use App \ BookStore \ Domain \ ValueObject \ BookDescription ;
use App \ BookStore \ Domain \ ValueObject \ BookId ;
use App \ BookStore \ Domain \ ValueObject \ BookName ;
use App \ BookStore \ Domain \ ValueObject \ Price ;
use App \ Shared \ Application \ Command \ CommandInterface ;
/**
* @implements CommandInterface<Book>
*/
final readonly class UpdateBookCommand implements CommandInterface
{
public function __construct (
public BookId $id ,
public ? BookName $name = null ,
public ? BookDescription $description = null ,
public ? Author $author = null ,
public ? BookContent $content = null ,
public ? Price $price = null ,
) {
}
}
In the same folder, we also have this existing FindBookQuery
:
src/BookStore/Application/Query/FindBookQuery.php
Copy
declare (strict_types = 1 );
namespace App \ BookStore \ Application \ Query ;
use App \ BookStore \ Domain \ Model \ Book ;
use App \ BookStore \ Domain \ ValueObject \ BookId ;
use App \ Shared \ Application \ Query \ QueryInterface ;
/**
* @implements QueryInterface<Book>
*/
final readonly class FindBookQuery implements QueryInterface
{
public function __construct (
public BookId $id ,
) {
}
}
The idea is to reuse these query and command to update the book in the storage for your "update" operation.
Create the BookItemProvider
First, we need to create the BookItemProvider
in order to fetch the right book.
src/BookStore/Infrastructure/Sylius/State/Provider/BookItemProvider.php
Copy
declare (strict_types = 1 );
namespace App \ BookStore \ Infrastructure \ Sylius \ State \ Provider ;
use App \ BookStore \ Application \ Query \ FindBookQuery ;
use App \ BookStore \ Domain \ ValueObject \ BookId ;
use App \ BookStore \ Infrastructure \ Sylius \ Resource \ BookResource ;
use App \ Shared \ Application \ Query \ QueryBusInterface ;
use Sylius \ Resource \ Context \ Context ;
use Sylius \ Resource \ Context \ Option \ RequestOption ;
use Sylius \ Resource \ Metadata \ Operation ;
use Sylius \ Resource \ State \ ProviderInterface ;
use Symfony \ Component \ Uid \ Uuid ;
/**
* @implements ProviderInterface<BookResource>
*/
final readonly class BookItemProvider implements ProviderInterface
{
public function __construct (
private QueryBusInterface $queryBus ,
) {
}
public function provide ( Operation $operation , Context $context) : object | array | null
{
$id = $context -> get ( RequestOption ::class )
?-> request ()
-> attributes
-> getString ( 'id' )
;
$model = $this -> queryBus -> ask ( new FindBookQuery ( new BookId ( Uuid :: fromString ( $id ) )) ) ;
return BookResource :: fromModel ( $model ) ;
}
}
Create the UpdateBookProcessor
We also need to create the UpdateBookProcessor
where we're going to call our UpdateBookCommand
.
src/BookStore/Infrastructure/Sylius/State/Processor/UpdateBookProcessor.php
Copy
declare (strict_types = 1 );
namespace App \ BookStore \ Infrastructure \ Sylius \ State \ Processor ;
use App \ BookStore \ Application \ Command \ UpdateBookCommand ;
use App \ BookStore \ Domain \ ValueObject \ Author ;
use App \ BookStore \ Domain \ ValueObject \ BookContent ;
use App \ BookStore \ Domain \ ValueObject \ BookDescription ;
use App \ BookStore \ Domain \ ValueObject \ BookId ;
use App \ BookStore \ Domain \ ValueObject \ BookName ;
use App \ BookStore \ Domain \ ValueObject \ Price ;
use App \ BookStore \ Infrastructure \ Sylius \ Resource \ BookResource ;
use App \ Shared \ Application \ Command \ CommandBusInterface ;
use Sylius \ Resource \ Context \ Context ;
use Sylius \ Resource \ Metadata \ Operation ;
use Sylius \ Resource \ State \ ProcessorInterface ;
use Webmozart \ Assert \ Assert ;
/**
* @implements ProcessorInterface<BookResource>
*/
final readonly class UpdateBookProcessor implements ProcessorInterface
{
public function __construct (
private CommandBusInterface $commandBus ,
) {
}
public function process ( mixed $data , Operation $operation , Context $context) : mixed
{
Assert :: isInstanceOf ( $data , BookResource ::class ) ;
$command = new UpdateBookCommand (
new BookId ($data -> id) ,
null !== $data -> name ? new BookName ($data -> name) : null ,
null !== $data -> description ? new BookDescription ($data -> description) : null ,
null !== $data -> author ? new Author ($data -> author) : null ,
null !== $data -> content ? new BookContent ($data -> content) : null ,
null !== $data -> price ? new Price ($data -> price) : null ,
);
$model = $this -> commandBus -> dispatch ( $command ) ;
return BookResource :: fromModel ( $model ) ;
}
}
Adding the provider & processor on the Book Resource
Now, we add the "update" operation on the BookResource
.
src/BookStore/Infrastructure/Sylius/Resource/BookResource.php
Copy // ...
use App \ BookStore \ Infrastructure \ Sylius \ State \ Processor \ UpdateBookProcessor ;
use App \ BookStore \ Infrastructure \ Sylius \ State \ Provider \ BookItemProvider ;
use App \ BookStore \ Infrastructure \ Symfony \ Form \ BookResourceType ;
use Sylius \ Resource \ Metadata \ Update ;
// ...
#[ AsResource (
// ...
formType : BookResourceType ::class , // Define the form type for all your operations
operations : [
// ...
new Update (
provider: BookItemProvider ::class , // the provider we have just created
processor: UpdateBookProcessor ::class , // the processor we have just created
// formType: UpdateBookResourceType::class, // Optional: define a specific form type only for the "update" operation
) ,
] ,
) ]
final class BookResource implements ResourceInterface
{
// ...
}
Book removal
Now that we can update an existing book, we also want to be able to delete it. In the Application folder, we already have this DeleteBookCommand
:
src/BookStore/Application/Command/DeleteBookCommand.php
Copy
declare (strict_types = 1 );
namespace App \ BookStore \ Application \ Command ;
use App \ BookStore \ Domain \ ValueObject \ BookId ;
use App \ Shared \ Application \ Command \ CommandInterface ;
/**
* @implements CommandInterface<void>
*/
final readonly class DeleteBookCommand implements CommandInterface
{
public function __construct (
public BookId $id ,
) {
}
}
Create the DeleteBookProcessor
We need to create the DeleteBookProcessor
.
src/BookStore/Infrastructure/Sylius/State/Processor/DeleteBookProcessor.php
Copy
declare (strict_types = 1 );
namespace App \ BookStore \ Infrastructure \ Sylius \ State \ Processor ;
use App \ BookStore \ Application \ Command \ DeleteBookCommand ;
use App \ BookStore \ Domain \ ValueObject \ BookId ;
use App \ BookStore \ Infrastructure \ Sylius \ Resource \ BookResource ;
use App \ Shared \ Application \ Command \ CommandBusInterface ;
use Sylius \ Resource \ Context \ Context ;
use Sylius \ Resource \ Metadata \ Operation ;
use Sylius \ Resource \ State \ ProcessorInterface ;
use Webmozart \ Assert \ Assert ;
/**
* @implements ProcessorInterface<null>
*/
final readonly class DeleteBookProcessor implements ProcessorInterface
{
public function __construct (
private CommandBusInterface $commandBus ,
) {
}
public function process ( mixed $data , Operation $operation , Context $context) : mixed
{
Assert :: isInstanceOf ( $data , BookResource ::class ) ;
$this -> commandBus -> dispatch ( new DeleteBookCommand ( new BookId ($data -> id)) ) ;
return null;
}
}
Adding the processor on the Book Resource
And then we create the "delete" operation on the BookResource.
src/BookStore/Infrastructure/Sylius/Resource/BookResource.php
Copy // ...
use App \ BookStore \ Infrastructure \ Sylius \ State \ Processor \ DeleteBookProcessor ;
use App \ BookStore \ Infrastructure \ Sylius \ State \ Provider \ BookItemProvider ;
use Sylius \ Resource \ Metadata \ Delete ;
// ...
#[ AsResource (
// ...
operations : [
// ...
new Delete (
provider: BookItemProvider ::class , // the provider we have already created in book creation
processor: DeleteBookProcessor ::class , // the processor we have just created
) ,
] ,
) ]
final class BookResource implements ResourceInterface
{
// ...
}