Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Resource revolution

Resource revolution

Slides of my talk during the Sylius Con 2023 event.

Loïc Frémont

November 07, 2023
Tweet

More Decks by Loïc Frémont

Other Decks in Programming

Transcript

  1. 1. Resource Revolution 2. Akawaka 3. What is Monofony and

    Why use it? 4. New Sylius Resource System 5. Sylius Resource without driver 6. What’s next?
  2. Akawaka Experts for your web projects & Sylius partner We

    help you design and improve your projects:
  3. Akawaka Experts for your web projects & Sylius partner We

    help you design and improve your projects: We use clean architecture techniques via DDD methodologies for projects that stand the test of time,
  4. Akawaka Experts for your web projects & Sylius partner We

    help you design and improve your projects: We use clean architecture techniques via DDD methodologies for projects that stand the test of time, A true quality approach through testing,
  5. Akawaka Experts for your web projects & Sylius partner We

    help you design and improve your projects: We use clean architecture techniques via DDD methodologies for projects that stand the test of time, A true quality approach through testing, Efficient industrialization adapted to your projects,
  6. Akawaka Experts for your web projects & Sylius partner We

    help you design and improve your projects: We use clean architecture techniques via DDD methodologies for projects that stand the test of time, A true quality approach through testing, Efficient industrialization adapted to your projects, On a contract or fixed-price basis, to create and/or integrate teams and work in complete collaboration.
  7. What is Monofony and Why use it? Bootstrapping a modern

    application on top of Symfony Leveraging Sylius bundles and components
  8. What is Monofony and Why use it? Bootstrapping a modern

    application on top of Symfony Leveraging Sylius bundles and components Helping you to focus more on what truly matters to your use-case
  9. Monofony API Pack voir l’installation détaillée dans la doc. $

    composer require monofony/api-pack "^0.10"
  10. Today use cases Better DX Customize persistence layer : ERP,

    Elastic search… Allow to use in DDD projects
  11. New Sylius Resource System API Platform for the inspiration Akawaka

    & Commerce Weavers for sponsoring that development
  12. New Sylius Resource System API Platform for the inspiration Akawaka

    & Commerce Weavers for sponsoring that development Łukasz Chruściel at Commerce Weavers for the code reviews
  13. Use the Resource attribute PHP attribute #[AsResource] configures your entity

    as a Sylius resource. ` ` namespace App\Entity; use App\Repository\BookRepository; use Doctrine\ORM\Mapping as ORM; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[ORM\Entity(repositoryClass: BookRepository::class)] #[AsResource] class Book implements ResourceInterface { }
  14. Use the Resource attribute PHP attribute #[AsResource] configures your entity

    as a Sylius resource. ` ` #[AsResource] namespace App\Entity; use App\Repository\BookRepository; use Doctrine\ORM\Mapping as ORM; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[ORM\Entity(repositoryClass: BookRepository::class)] class Book implements ResourceInterface { }
  15. Use the Resource attribute PHP attribute #[AsResource] configures your entity

    as a Sylius resource. ` ` use Sylius\Resource\Metadata\AsResource; #[AsResource] namespace App\Entity; use App\Repository\BookRepository; use Doctrine\ORM\Mapping as ORM; use Sylius\Resource\Model\ResourceInterface; #[ORM\Entity(repositoryClass: BookRepository::class)] class Book implements ResourceInterface { }
  16. Debug command Output bin/console sylius:debug:resource 'app.book' New Resource Metadata ---------------------

    ------------------------ ------------------- Option Value ------------------------ ------------------- alias "app.book" section null formType null templatesDir null routePrefix null name "book" pluralName null applicationName "app" identifier null normalizationContext null denormalizationContext null validationContext null class "App\Entity\Book" ------------------------ -------------------
  17. Browsing books We’ll use Index operation which allows to browse

    all items of your resource. ` ` namespace App\Entity; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource( operations: [ new Index(), ], )] // OR #[AsResource] #[Index] class Book implements ResourceInterface { }
  18. Browsing books We’ll use Index operation which allows to browse

    all items of your resource. ` ` operations: [ new Index(), ], namespace App\Entity; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource( )] // OR #[AsResource] #[Index] class Book implements ResourceInterface { }
  19. Browsing books We’ll use Index operation which allows to browse

    all items of your resource. ` ` use Sylius\Resource\Metadata\Index; operations: [ new Index(), ], namespace App\Entity; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource( )] // OR #[AsResource] #[Index] class Book implements ResourceInterface { }
  20. Browsing books We’ll use Index operation which allows to browse

    all items of your resource. ` ` // OR #[AsResource] #[Index] namespace App\Entity; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource( operations: [ new Index(), ], )] class Book implements ResourceInterface { }
  21. Route It will configure this route for your index operation.

    Name Method Path app_book_index GET /books ` `
  22. Tips bin/console sylius:debug:resource app_book_index Operation Metadata ------------------ ------------------------ -------------------------------------------------------------------- Option

    Value ------------------------ -------------------------------------------------------------------- twigContextFactory "sylius.twig.context.factory.default" methods [ "GET" ] path null routeName "app_book_index" routePrefix null redirectToRoute null redirectArguments null provider "Sylius\Resource\Symfony\Request\State\Provider" processor "Sylius\Resource\Doctrine\Common\State\PersistProcessor" responder "Sylius\Resource\Symfony\Request\State\Responder" repository "app.repository.book" template "@SyliusAdminUi/crud/index.html.twig" #[...]
  23. final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { public static

    function getName(): string { return 'app_book'; } public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( StringField::create('name') ->setLabel('sylius.ui.name') ->setSortable(true) ) ->addField( StringField::create('author') ->setLabel('sylius.ui.author') ->setSortable(true) ) ; }
  24. final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { public static

    function getName(): string { return 'app_book'; } public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( StringField::create('name') ->setLabel('sylius.ui.name') ->setSortable(true) ) ->addField( StringField::create('author') ->setLabel('sylius.ui.author') ->setSortable(true) ) ; }
  25. public static function getName(): string { return 'app_book'; } final

    class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( StringField::create('name') ->setLabel('sylius.ui.name') ->setSortable(true) ) ->addField( StringField::create('author') ->setLabel('sylius.ui.author') ->setSortable(true) ) ; }
  26. ->orderBy('name', 'asc') final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface {

    public static function getName(): string { return 'app_book'; } public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->addField( StringField::create('name') ->setLabel('sylius.ui.name') ->setSortable(true) ) ->addField( StringField::create('author') ->setLabel('sylius.ui.author') ->setSortable(true) ) ; }
  27. ->addField( StringField::create('name') ->setLabel('sylius.ui.name') ->setSortable(true) ) final class BookGrid extends AbstractGrid

    implements ResourceAwareGridInterface { public static function getName(): string { return 'app_book'; } public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( StringField::create('author') ->setLabel('sylius.ui.author') ->setSortable(true) ) ; }
  28. ->addField( StringField::create('author') ->setLabel('sylius.ui.author') ->setSortable(true) ) final class BookGrid extends AbstractGrid

    implements ResourceAwareGridInterface { public static function getName(): string { return 'app_book'; } public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( StringField::create('name') ->setLabel('sylius.ui.name') ->setSortable(true) ) ; }
  29. Use this grid for your index operation To use a

    grid for you operation, you need to install the Sylius grid package
  30. Use this grid for your index operation To use a

    grid for you operation, you need to install the Sylius grid package namespace App\Entity; use App\Grid\BookGrid; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource] // You can use either the FQCN of your grid #[Index(grid: BookGrid::class)] // Or you can use the grid name #[Index(grid: 'app_book')] class Book implements ResourceInterface { }
  31. Use this grid for your index operation To use a

    grid for you operation, you need to install the Sylius grid package // You can use either the FQCN of your grid #[Index(grid: BookGrid::class)] namespace App\Entity; use App\Grid\BookGrid; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource] // Or you can use the grid name #[Index(grid: 'app_book')] class Book implements ResourceInterface { }
  32. Use this grid for your index operation To use a

    grid for you operation, you need to install the Sylius grid package use App\Grid\BookGrid; // You can use either the FQCN of your grid #[Index(grid: BookGrid::class)] namespace App\Entity; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource] // Or you can use the grid name #[Index(grid: 'app_book')] class Book implements ResourceInterface { }
  33. Use this grid for your index operation To use a

    grid for you operation, you need to install the Sylius grid package // Or you can use the grid name #[Index(grid: 'app_book')] namespace App\Entity; use App\Grid\BookGrid; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource] // You can use either the FQCN of your grid #[Index(grid: BookGrid::class)] class Book implements ResourceInterface { }
  34. Use this grid for your index operation To use a

    grid for you operation, you need to install the Sylius grid package // Or you can use the grid name #[Index(grid: 'app_book')] namespace App\Entity; use App\Grid\BookGrid; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource] // You can use either the FQCN of your grid #[Index(grid: BookGrid::class)] class Book implements ResourceInterface { }
  35. bin/console sylius:debug:resource app_book_index Operation Metadata ------------------ ------------------------ -------------------------------------------------------------------- Option Value

    ------------------------ -------------------------------------------------------------------- #[...] provider "Sylius\Resource\Grid\State\RequestGridProvider" #[...]
  36. Adding/Editing books We’ll use Create and Update operations which allows

    to add a new item of your resource. ` ` ` ` namespace App\Entity; use App\Form\BookType; use Sylius\Resource\Metadata\Create; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\Update; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource( formType: BookType::class, operations: [ new Index(grid: BookGrid::class), new Create(), new Update(), ], )] class Book implements ResourceInterface { }
  37. Adding/Editing books We’ll use Create and Update operations which allows

    to add a new item of your resource. ` ` ` ` formType: BookType::class, namespace App\Entity; use App\Form\BookType; use Sylius\Resource\Metadata\Create; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\Update; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource( operations: [ new Index(grid: BookGrid::class), new Create(), new Update(), ], )] class Book implements ResourceInterface { }
  38. Adding/Editing books We’ll use Create and Update operations which allows

    to add a new item of your resource. ` ` ` ` use App\Form\BookType; formType: BookType::class, namespace App\Entity; use Sylius\Resource\Metadata\Create; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\Update; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource( operations: [ new Index(grid: BookGrid::class), new Create(), new Update(), ], )] class Book implements ResourceInterface { }
  39. Adding/Editing books We’ll use Create and Update operations which allows

    to add a new item of your resource. ` ` ` ` new Create(), new Update(), namespace App\Entity; use App\Form\BookType; use Sylius\Resource\Metadata\Create; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\Update; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource( formType: BookType::class, operations: [ new Index(grid: BookGrid::class), ], )] class Book implements ResourceInterface { }
  40. Adding/Editing books We’ll use Create and Update operations which allows

    to add a new item of your resource. ` ` ` ` use Sylius\Resource\Metadata\Create; use Sylius\Resource\Metadata\Update; new Create(), new Update(), namespace App\Entity; use App\Form\BookType; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; #[AsResource( formType: BookType::class, operations: [ new Index(grid: BookGrid::class), ], )] class Book implements ResourceInterface { }
  41. Route It will configure this route for your create and

    your update operations. Name Method Path app_book_create GET, POST /books/new app_book_update GET, PUT, PATCH, POST /books/{id}/edit ` ` ` `
  42. final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { // [...]

    public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( // [...] ) ->addField( // [...] ) ->addActionGroup( MainActionGroup::create( CreateAction::create(), ) ) ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ) ) ; }
  43. ->addActionGroup( MainActionGroup::create( CreateAction::create(), ) ) final class BookGrid extends AbstractGrid

    implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( // [...] ) ->addField( // [...] ) ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ) ) ; }
  44. MainActionGroup::create( final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( // [...] ) ->addField( // [...] ) ->addActionGroup( CreateAction::create(), ) ) ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ) ) ; }
  45. CreateAction::create(), final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( // [...] ) ->addField( // [...] ) ->addActionGroup( MainActionGroup::create( ) ) ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ) ) ; }
  46. ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ) ) final class BookGrid extends AbstractGrid

    implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( // [...] ) ->addField( // [...] ) ->addActionGroup( MainActionGroup::create( CreateAction::create(), ) ) ; }
  47. ItemActionGroup::create( final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( // [...] ) ->addField( // [...] ) ->addActionGroup( MainActionGroup::create( CreateAction::create(), ) ) ->addActionGroup( UpdateAction::create(), ) ) ; }
  48. UpdateAction::create(), final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->orderBy('name', 'asc') ->addField( // [...] ) ->addField( // [...] ) ->addActionGroup( MainActionGroup::create( CreateAction::create(), ) ) ->addActionGroup( ItemActionGroup::create( ) ) ; }
  49. Removing books We’ll use Delete and Bulk delete operations which

    allows to remove existing items of your resource. ` ` ` `
  50. Removing books We’ll use Delete and Bulk delete operations which

    allows to remove existing items of your resource. ` ` ` ` namespace App\Entity; use App\Form\BookType; use Sylius\Resource\Metadata\BulkDelete; use Sylius\Resource\Metadata\Create; use Sylius\Resource\Metadata\Delete; // [...] #[AsResource( formType: BookType::class, operations: [ new Index(grid: BookGrid::class), new Create(), new Update(), new Delete(), new BulkDelete(), ], )] class Book implements ResourceInterface { }
  51. Removing books We’ll use Delete and Bulk delete operations which

    allows to remove existing items of your resource. ` ` ` ` new Delete(), new BulkDelete(), namespace App\Entity; use App\Form\BookType; use Sylius\Resource\Metadata\BulkDelete; use Sylius\Resource\Metadata\Create; use Sylius\Resource\Metadata\Delete; // [...] #[AsResource( formType: BookType::class, operations: [ new Index(grid: BookGrid::class), new Create(), new Update(), ], )] class Book implements ResourceInterface { }
  52. Removing books We’ll use Delete and Bulk delete operations which

    allows to remove existing items of your resource. ` ` ` ` use Sylius\Resource\Metadata\BulkDelete; use Sylius\Resource\Metadata\Delete; new Delete(), new BulkDelete(), namespace App\Entity; use App\Form\BookType; use Sylius\Resource\Metadata\Create; // [...] #[AsResource( formType: BookType::class, operations: [ new Index(grid: BookGrid::class), new Create(), new Update(), ], )] class Book implements ResourceInterface { }
  53. Route It will configure this route for your delete and

    bulk_delete operations. Name Method Path app_book_delete DELETE, POST /books/{id}/delete app_book_bulk_delete DELETE, POST /books/bulk_delete ` ` ` `
  54. final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { // [...]

    public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( // [...] ) ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), DeleteAction::create(), ) ) ->addActionGroup( BulkActionGroup::create( DeleteAction::create() ) ) ; } public function getResourceClass(): string
  55. ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), DeleteAction::create(), ) ) final class BookGrid extends

    AbstractGrid implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( // [...] ) ->addActionGroup( BulkActionGroup::create( DeleteAction::create() ) ) ; } public function getResourceClass(): string
  56. UpdateAction::create(), final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( // [...] ) ->addActionGroup( ItemActionGroup::create( DeleteAction::create(), ) ) ->addActionGroup( BulkActionGroup::create( DeleteAction::create() ) ) ; } public function getResourceClass(): string
  57. DeleteAction::create(), final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( // [...] ) ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ) ) ->addActionGroup( BulkActionGroup::create( DeleteAction::create() ) ) ; } public function getResourceClass(): string
  58. ->addActionGroup( BulkActionGroup::create( DeleteAction::create() ) ) final class BookGrid extends AbstractGrid

    implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( // [...] ) ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), DeleteAction::create(), ) ) ; } public function getResourceClass(): string
  59. BulkActionGroup::create( final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( // [...] ) ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), DeleteAction::create(), ) ) ->addActionGroup( DeleteAction::create() ) ) ; } public function getResourceClass(): string
  60. DeleteAction::create() final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( // [...] ) ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), DeleteAction::create(), ) ) ->addActionGroup( BulkActionGroup::create( ) ) ; } public function getResourceClass(): string
  61. Publishing books We’ll use apply_state_machine_transition operation which allows to apply

    a transition using a state machine. ` ` namespace App\Entity; use Sylius\Resource\Metadata\ApplyStateMachineTransition; use Sylius\Resource\Model\ResourceInterface; // [...] #[ApplyStateMachineTransition(stateMachineTransition: 'publish')] class Book implements ResourceInterface { }
  62. Publishing books We’ll use apply_state_machine_transition operation which allows to apply

    a transition using a state machine. ` ` #[ApplyStateMachineTransition(stateMachineTransition: 'publish')] namespace App\Entity; use Sylius\Resource\Metadata\ApplyStateMachineTransition; use Sylius\Resource\Model\ResourceInterface; // [...] class Book implements ResourceInterface { }
  63. Publishing books We’ll use apply_state_machine_transition operation which allows to apply

    a transition using a state machine. ` ` use Sylius\Resource\Metadata\ApplyStateMachineTransition; #[ApplyStateMachineTransition(stateMachineTransition: 'publish')] namespace App\Entity; use Sylius\Resource\Model\ResourceInterface; // [...] class Book implements ResourceInterface { }
  64. Route It will configure this route for your apply_state_machine_transition operation.

    Name Method Path app_book_publish PUT, PATCH, POST /books/{id}/publish ` `
  65. final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { // [...]

    public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addField( StringField::create('author') ->setLabel('sylius.ui.author') ->setSortable(true) ) ->addField( TwigField::create('state', '@SyliusUi/Grid/Field/state.html.twig') ->setLabel('sylius.ui.state') ->setOption('vars', ['labels' => 'admin/book/label/state']), ) ; } public function getResourceClass(): string { return Book::class; } }
  66. ->addField( TwigField::create('state', '@SyliusUi/Grid/Field/state.html.twig') ->setLabel('sylius.ui.state') ->setOption('vars', ['labels' => 'admin/book/label/state']), ) ;

    final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addField( StringField::create('author') ->setLabel('sylius.ui.author') ->setSortable(true) ) } public function getResourceClass(): string { return Book::class; } }
  67. TwigField::create('state', '@SyliusUi/Grid/Field/state.html.twig') final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface {

    // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addField( StringField::create('author') ->setLabel('sylius.ui.author') ->setSortable(true) ) ->addField( ->setLabel('sylius.ui.state') ->setOption('vars', ['labels' => 'admin/book/label/state']), ) ; } public function getResourceClass(): string { return Book::class; } }
  68. ->setLabel('sylius.ui.state') final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addField( StringField::create('author') ->setLabel('sylius.ui.author') ->setSortable(true) ) ->addField( TwigField::create('state', '@SyliusUi/Grid/Field/state.html.twig') ->setOption('vars', ['labels' => 'admin/book/label/state']), ) ; } public function getResourceClass(): string { return Book::class; } }
  69. ->setOption('vars', ['labels' => 'admin/book/label/state']), final class BookGrid extends AbstractGrid implements

    ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addField( StringField::create('author') ->setLabel('sylius.ui.author') ->setSortable(true) ) ->addField( TwigField::create('state', '@SyliusUi/Grid/Field/state.html.twig') ->setLabel('sylius.ui.state') ) ; } public function getResourceClass(): string { return Book::class; } }
  70. final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { // [...]

    public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ApplyTransitionAction::create( name: 'publish', route: 'app_admin_book_publish', routeParameters: ['id' => 'resource.id'], options: [ 'class' => 'green', ], ) ->setLabel('app.ui.publish') ->setIcon('checkmark'), DeleteAction::create(), ) ) ; }
  71. ApplyTransitionAction::create( name: 'publish', route: 'app_admin_book_publish', routeParameters: ['id' => 'resource.id'], options:

    [ 'class' => 'green', ], ) ->setLabel('app.ui.publish') ->setIcon('checkmark'), final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), DeleteAction::create(), ) ) ; }
  72. ApplyTransitionAction::create( final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), name: 'publish', route: 'app_admin_book_publish', routeParameters: ['id' => 'resource.id'], options: [ 'class' => 'green', ], ) ->setLabel('app.ui.publish') ->setIcon('checkmark'), DeleteAction::create(), ) ) ; }
  73. name: 'publish', final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface {

    // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ApplyTransitionAction::create( route: 'app_admin_book_publish', routeParameters: ['id' => 'resource.id'], options: [ 'class' => 'green', ], ) ->setLabel('app.ui.publish') ->setIcon('checkmark'), DeleteAction::create(), ) ) ; }
  74. route: 'app_admin_book_publish', final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface {

    // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ApplyTransitionAction::create( name: 'publish', routeParameters: ['id' => 'resource.id'], options: [ 'class' => 'green', ], ) ->setLabel('app.ui.publish') ->setIcon('checkmark'), DeleteAction::create(), ) ) ; }
  75. routeParameters: ['id' => 'resource.id'], final class BookGrid extends AbstractGrid implements

    ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ApplyTransitionAction::create( name: 'publish', route: 'app_admin_book_publish', options: [ 'class' => 'green', ], ) ->setLabel('app.ui.publish') ->setIcon('checkmark'), DeleteAction::create(), ) ) ; }
  76. options: [ 'class' => 'green', ], final class BookGrid extends

    AbstractGrid implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ApplyTransitionAction::create( name: 'publish', route: 'app_admin_book_publish', routeParameters: ['id' => 'resource.id'], ) ->setLabel('app.ui.publish') ->setIcon('checkmark'), DeleteAction::create(), ) ) ; }
  77. ->setLabel('app.ui.publish') final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ApplyTransitionAction::create( name: 'publish', route: 'app_admin_book_publish', routeParameters: ['id' => 'resource.id'], options: [ 'class' => 'green', ], ) ->setIcon('checkmark'), DeleteAction::create(), ) ) ; }
  78. ->setIcon('checkmark'), final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { //

    [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( ItemActionGroup::create( UpdateAction::create(), ApplyTransitionAction::create( name: 'publish', route: 'app_admin_book_publish', routeParameters: ['id' => 'resource.id'], options: [ 'class' => 'green', ], ) ->setLabel('app.ui.publish') DeleteAction::create(), ) ) ; }
  79. Publishing books with custom processor We configure an update operation.

    ` ` namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Metadata\Update; use Sylius\Resource\Model\ResourceInterface; // [...] #[Update( methods: ['PUT', 'PATCH', 'POST'], shortName: 'publish', processor: PublishBookProcessor::class, validate: false, )] class Book implements ResourceInterface { }
  80. Publishing books with custom processor We configure an update operation.

    ` ` #[Update( methods: ['PUT', 'PATCH', 'POST'], shortName: 'publish', processor: PublishBookProcessor::class, validate: false, )] namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Metadata\Update; use Sylius\Resource\Model\ResourceInterface; // [...] class Book implements ResourceInterface { }
  81. Publishing books with custom processor We configure an update operation.

    ` ` use Sylius\Resource\Metadata\Update; #[Update( methods: ['PUT', 'PATCH', 'POST'], shortName: 'publish', processor: PublishBookProcessor::class, validate: false, )] namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Model\ResourceInterface; // [...] class Book implements ResourceInterface { }
  82. Publishing books with custom processor We configure an update operation.

    ` ` methods: ['PUT', 'PATCH', 'POST'], namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Metadata\Update; use Sylius\Resource\Model\ResourceInterface; // [...] #[Update( shortName: 'publish', processor: PublishBookProcessor::class, validate: false, )] class Book implements ResourceInterface { }
  83. Publishing books with custom processor We configure an update operation.

    ` ` shortName: 'publish', namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Metadata\Update; use Sylius\Resource\Model\ResourceInterface; // [...] #[Update( methods: ['PUT', 'PATCH', 'POST'], processor: PublishBookProcessor::class, validate: false, )] class Book implements ResourceInterface { }
  84. Publishing books with custom processor We configure an update operation.

    ` ` processor: PublishBookProcessor::class, namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Metadata\Update; use Sylius\Resource\Model\ResourceInterface; // [...] #[Update( methods: ['PUT', 'PATCH', 'POST'], shortName: 'publish', validate: false, )] class Book implements ResourceInterface { }
  85. Publishing books with custom processor We configure an update operation.

    ` ` use App\State\Processor\PublishBookProcessor; processor: PublishBookProcessor::class, namespace App\Entity; use Sylius\Resource\Metadata\Update; use Sylius\Resource\Model\ResourceInterface; // [...] #[Update( methods: ['PUT', 'PATCH', 'POST'], shortName: 'publish', validate: false, )] class Book implements ResourceInterface { }
  86. Publishing books with custom processor We configure an update operation.

    ` ` validate: false, namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Metadata\Update; use Sylius\Resource\Model\ResourceInterface; // [...] #[Update( methods: ['PUT', 'PATCH', 'POST'], shortName: 'publish', processor: PublishBookProcessor::class, )] class Book implements ResourceInterface { }
  87. Route It will configure this route for your publish operation.

    Name Method Path app_book_publish PUT, PATCH, POST /books/{id}/publish ` `
  88. // src/State/Processor/PublishBookProcessor.php use Sylius\Resource\Context\Context; use Sylius\Resource\Doctrine\Common\State\PersistProcessor; use Sylius\Resource\Metadata\Operation; use Sylius\Resource\State\ProcessorInterface;

    use Symfony\\Workflow\WorkflowInterface; final class PublishBookProcessor implements ProcessorInterface { public function __construct( private readonly WorkflowInterface $bookPublishingStateMachine, private readonly PersistProcessor $persistProcessor, ) { } public function process(mixed $data, Operation $operation, Context $context): mixed { if ($this->bookPublishingStateMachine->can($data, 'publish')) { $this->bookPublishingStateMachine->apply($data, 'publish'); } return $this->persistProcessor->process($data, $operation, $context); } }
  89. final class PublishBookProcessor implements ProcessorInterface // src/State/Processor/PublishBookProcessor.php use Sylius\Resource\Context\Context; use

    Sylius\Resource\Doctrine\Common\State\PersistProcessor; use Sylius\Resource\Metadata\Operation; use Sylius\Resource\State\ProcessorInterface; use Symfony\\Workflow\WorkflowInterface; { public function __construct( private readonly WorkflowInterface $bookPublishingStateMachine, private readonly PersistProcessor $persistProcessor, ) { } public function process(mixed $data, Operation $operation, Context $context): mixed { if ($this->bookPublishingStateMachine->can($data, 'publish')) { $this->bookPublishingStateMachine->apply($data, 'publish'); } return $this->persistProcessor->process($data, $operation, $context); } }
  90. use Sylius\Resource\State\ProcessorInterface; final class PublishBookProcessor implements ProcessorInterface // src/State/Processor/PublishBookProcessor.php use

    Sylius\Resource\Context\Context; use Sylius\Resource\Doctrine\Common\State\PersistProcessor; use Sylius\Resource\Metadata\Operation; use Symfony\\Workflow\WorkflowInterface; { public function __construct( private readonly WorkflowInterface $bookPublishingStateMachine, private readonly PersistProcessor $persistProcessor, ) { } public function process(mixed $data, Operation $operation, Context $context): mixed { if ($this->bookPublishingStateMachine->can($data, 'publish')) { $this->bookPublishingStateMachine->apply($data, 'publish'); } return $this->persistProcessor->process($data, $operation, $context); } }
  91. private readonly WorkflowInterface $bookPublishingStateMachine, // src/State/Processor/PublishBookProcessor.php use Sylius\Resource\Context\Context; use Sylius\Resource\Doctrine\Common\State\PersistProcessor;

    use Sylius\Resource\Metadata\Operation; use Sylius\Resource\State\ProcessorInterface; use Symfony\\Workflow\WorkflowInterface; final class PublishBookProcessor implements ProcessorInterface { public function __construct( private readonly PersistProcessor $persistProcessor, ) { } public function process(mixed $data, Operation $operation, Context $context): mixed { if ($this->bookPublishingStateMachine->can($data, 'publish')) { $this->bookPublishingStateMachine->apply($data, 'publish'); } return $this->persistProcessor->process($data, $operation, $context); } }
  92. use Symfony\\Workflow\WorkflowInterface; private readonly WorkflowInterface $bookPublishingStateMachine, // src/State/Processor/PublishBookProcessor.php use Sylius\Resource\Context\Context;

    use Sylius\Resource\Doctrine\Common\State\PersistProcessor; use Sylius\Resource\Metadata\Operation; use Sylius\Resource\State\ProcessorInterface; final class PublishBookProcessor implements ProcessorInterface { public function __construct( private readonly PersistProcessor $persistProcessor, ) { } public function process(mixed $data, Operation $operation, Context $context): mixed { if ($this->bookPublishingStateMachine->can($data, 'publish')) { $this->bookPublishingStateMachine->apply($data, 'publish'); } return $this->persistProcessor->process($data, $operation, $context); } }
  93. private readonly PersistProcessor $persistProcessor, // src/State/Processor/PublishBookProcessor.php use Sylius\Resource\Context\Context; use Sylius\Resource\Doctrine\Common\State\PersistProcessor;

    use Sylius\Resource\Metadata\Operation; use Sylius\Resource\State\ProcessorInterface; use Symfony\\Workflow\WorkflowInterface; final class PublishBookProcessor implements ProcessorInterface { public function __construct( private readonly WorkflowInterface $bookPublishingStateMachine, ) { } public function process(mixed $data, Operation $operation, Context $context): mixed { if ($this->bookPublishingStateMachine->can($data, 'publish')) { $this->bookPublishingStateMachine->apply($data, 'publish'); } return $this->persistProcessor->process($data, $operation, $context); } }
  94. use Sylius\Resource\Doctrine\Common\State\PersistProcessor; private readonly PersistProcessor $persistProcessor, // src/State/Processor/PublishBookProcessor.php use Sylius\Resource\Context\Context;

    use Sylius\Resource\Metadata\Operation; use Sylius\Resource\State\ProcessorInterface; use Symfony\\Workflow\WorkflowInterface; final class PublishBookProcessor implements ProcessorInterface { public function __construct( private readonly WorkflowInterface $bookPublishingStateMachine, ) { } public function process(mixed $data, Operation $operation, Context $context): mixed { if ($this->bookPublishingStateMachine->can($data, 'publish')) { $this->bookPublishingStateMachine->apply($data, 'publish'); } return $this->persistProcessor->process($data, $operation, $context); } }
  95. public function process(mixed $data, Operation $operation, Context $context): mixed //

    src/State/Processor/PublishBookProcessor.php use Sylius\Resource\Context\Context; use Sylius\Resource\Doctrine\Common\State\PersistProcessor; use Sylius\Resource\Metadata\Operation; use Sylius\Resource\State\ProcessorInterface; use Symfony\\Workflow\WorkflowInterface; final class PublishBookProcessor implements ProcessorInterface { public function __construct( private readonly WorkflowInterface $bookPublishingStateMachine, private readonly PersistProcessor $persistProcessor, ) { } { if ($this->bookPublishingStateMachine->can($data, 'publish')) { $this->bookPublishingStateMachine->apply($data, 'publish'); } return $this->persistProcessor->process($data, $operation, $context); } }
  96. if ($this->bookPublishingStateMachine->can($data, 'publish')) { // src/State/Processor/PublishBookProcessor.php use Sylius\Resource\Context\Context; use Sylius\Resource\Doctrine\Common\State\PersistProcessor;

    use Sylius\Resource\Metadata\Operation; use Sylius\Resource\State\ProcessorInterface; use Symfony\\Workflow\WorkflowInterface; final class PublishBookProcessor implements ProcessorInterface { public function __construct( private readonly WorkflowInterface $bookPublishingStateMachine, private readonly PersistProcessor $persistProcessor, ) { } public function process(mixed $data, Operation $operation, Context $context): mixed { $this->bookPublishingStateMachine->apply($data, 'publish'); } return $this->persistProcessor->process($data, $operation, $context); } }
  97. $this->bookPublishingStateMachine->apply($data, 'publish'); // src/State/Processor/PublishBookProcessor.php use Sylius\Resource\Context\Context; use Sylius\Resource\Doctrine\Common\State\PersistProcessor; use Sylius\Resource\Metadata\Operation;

    use Sylius\Resource\State\ProcessorInterface; use Symfony\\Workflow\WorkflowInterface; final class PublishBookProcessor implements ProcessorInterface { public function __construct( private readonly WorkflowInterface $bookPublishingStateMachine, private readonly PersistProcessor $persistProcessor, ) { } public function process(mixed $data, Operation $operation, Context $context): mixed { if ($this->bookPublishingStateMachine->can($data, 'publish')) { } return $this->persistProcessor->process($data, $operation, $context); } }
  98. return $this->persistProcessor->process($data, $operation, $context); // src/State/Processor/PublishBookProcessor.php use Sylius\Resource\Context\Context; use Sylius\Resource\Doctrine\Common\State\PersistProcessor;

    use Sylius\Resource\Metadata\Operation; use Sylius\Resource\State\ProcessorInterface; use Symfony\\Workflow\WorkflowInterface; final class PublishBookProcessor implements ProcessorInterface { public function __construct( private readonly WorkflowInterface $bookPublishingStateMachine, private readonly PersistProcessor $persistProcessor, ) { } public function process(mixed $data, Operation $operation, Context $context): mixed { if ($this->bookPublishingStateMachine->can($data, 'publish')) { $this->bookPublishingStateMachine->apply($data, 'publish'); } } }
  99. Publishing many books We’ll use Bulk Update operation which allows

    to update several items of your resource at the same time. ` `
  100. Publishing many books We’ll use Bulk Update operation which allows

    to update several items of your resource at the same time. ` ` namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Metadata\BulkUpdate; // [...] #[BulkUpdate( shortName: 'bulk_publish', processor: PublishBookProcessor::class, validate: false, )] class Book implements ResourceInterface { }
  101. Publishing many books We’ll use Bulk Update operation which allows

    to update several items of your resource at the same time. ` ` #[BulkUpdate( namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Metadata\BulkUpdate; // [...] shortName: 'bulk_publish', processor: PublishBookProcessor::class, validate: false, )] class Book implements ResourceInterface { }
  102. Publishing many books We’ll use Bulk Update operation which allows

    to update several items of your resource at the same time. ` ` use Sylius\Resource\Metadata\BulkUpdate; #[BulkUpdate( namespace App\Entity; use App\State\Processor\PublishBookProcessor; // [...] shortName: 'bulk_publish', processor: PublishBookProcessor::class, validate: false, )] class Book implements ResourceInterface { }
  103. Publishing many books We’ll use Bulk Update operation which allows

    to update several items of your resource at the same time. ` ` shortName: 'bulk_publish', namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Metadata\BulkUpdate; // [...] #[BulkUpdate( processor: PublishBookProcessor::class, validate: false, )] class Book implements ResourceInterface { }
  104. Publishing many books We’ll use Bulk Update operation which allows

    to update several items of your resource at the same time. ` ` processor: PublishBookProcessor::class, namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Metadata\BulkUpdate; // [...] #[BulkUpdate( shortName: 'bulk_publish', validate: false, )] class Book implements ResourceInterface { }
  105. Publishing many books We’ll use Bulk Update operation which allows

    to update several items of your resource at the same time. ` ` validate: false, namespace App\Entity; use App\State\Processor\PublishBookProcessor; use Sylius\Resource\Metadata\BulkUpdate; // [...] #[BulkUpdate( shortName: 'bulk_publish', processor: PublishBookProcessor::class, )] class Book implements ResourceInterface { }
  106. Route It will configure this route for your bulk_publish operation.

    Name Method Path app_book_bulk_publish PUT, PATCH, POST /books/bulk_publish ` `
  107. final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { // [...]

    public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( BulkActionGroup::create( DeleteAction::create(), Action::create('publish', 'apply_transition') ->setLabel('app.ui.publish') ->setIcon('icon: checkmark') ->setOptions([ 'link' => [ 'route' => 'app_admin_book_bulk_publish', ], 'class' => 'green', ]), ) ) ; } public function getResourceClass(): string
  108. Action::create('publish', 'apply_transition') ->setLabel('app.ui.publish') ->setIcon('icon: checkmark') ->setOptions([ 'link' => [ 'route'

    => 'app_admin_book_bulk_publish', ], 'class' => 'green', ]), ) ) ; } final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( BulkActionGroup::create( DeleteAction::create(), public function getResourceClass(): string
  109. Action::create('publish', 'apply_transition') final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface {

    // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( BulkActionGroup::create( DeleteAction::create(), ->setLabel('app.ui.publish') ->setIcon('icon: checkmark') ->setOptions([ 'link' => [ 'route' => 'app_admin_book_bulk_publish', ], 'class' => 'green', ]), ) ) ; } public function getResourceClass(): string
  110. ->setOptions([ 'link' => [ 'route' => 'app_admin_book_bulk_publish', ], 'class' =>

    'green', ]), ) final class BookGrid extends AbstractGrid implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( BulkActionGroup::create( DeleteAction::create(), Action::create('publish', 'apply_transition') ->setLabel('app.ui.publish') ->setIcon('icon: checkmark') ) ; } public function getResourceClass(): string
  111. 'link' => [ 'route' => 'app_admin_book_bulk_publish', ], final class BookGrid

    extends AbstractGrid implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder // [...] ->addActionGroup( BulkActionGroup::create( DeleteAction::create(), Action::create('publish', 'apply_transition') ->setLabel('app.ui.publish') ->setIcon('icon: checkmark') ->setOptions([ 'class' => 'green', ]), ) ) ; } public function getResourceClass(): string
  112. Resource with data from a CSV file config/data/board_games.csv 6b2fff2b-0b43-489b-8c48-9c0427a1c4c7,Stone Age,"Travel

    to the time of hunters and gatherers in this classic game of tool d068029a-0c32-4728-95bf-5f614d53440b,Ticket to Ride,"Build your railroad across North America to connect cities and comp 5ba27cbd-e230-48b7-86ae-ad2eaa05ebc0,7 Wonders,"Draft cards to develop your ancient civilization and build its Wonder of 2919785a-66e7-44da-b356-b98e07fbb9e8,Puerto Rico,"Ship goods, construct buildings, and choose roles that benefit you mor c4856c93-3a41-40eb-9994-f58a1035d99b,Azul, "Artfully embellish the walls of your palace by drafting the most beautiful t a953d760-07d5-4add-b87e-b483ebb194df,Die Fürsten von Florenz,"Attract artisans and scholars to your palazzo by building ce461300-25c3-4a72-aa7d-6ec7021d7b80,The Voyages of Marco Polo,"Using unique abilities, fulfill contracts and reach your 599dcd98-8167-4f4a-b181-efd7c24bb434,Tigris & Euphrates,"Keep your Mesopotamian civilisation in perfect balance through 305749e6-7a05-468a-b1aa-46ee6c1a89ac,Patchwork,"Piece together a quilt and leave no holes to become the button master." 5e980dad-d8ef-4e47-910a-4dc9f91b38fd,Cartagena,"Groups of pirates race to reach their ship and escape from Cartegena." 6a47110b-b65d-4e94-9ce0-a06cd7823ecc,Village,"Send your villagers to work, travel, pray, and... die when it brings the m e75c5edd-bf6a-49f8-a409-90183aea660c,Splendor,"Renaissance merchants race to grab gems, acquire property, and please nob 74c0d7ba-8acf-46eb-a6a7-c8f04c1860ff,Ra,"Bid to acquire the most valuable sets of Egyptian artifacts and resources." 44bc972e-6a0e-423a-9140-caae3f7f8710,Carcassonne,"Shape the medieval landscape of France, claiming cities, monasteries a 0a937d58-24dd-4dae-894a-b600dfa334d4,Modern Art,"Four types of auctions challenge players in this classic game of art sp c3661009-d024-4c83-8ef6-a967a0a21e6f,Goa,"Run a spice trade business in colonial-era India in this closed-loop economy g edbb5801-f1ae-4c14-b86a-130a7c3a1c4a,Shogun,"Gain control of medieval Japan by amassing troops and sending them out to b 1dec8957-3203-4f84-95c5-ceb3754e4b50,Samurai,"Dispute the favor of three different castes in order to unite Japan under 110c7432-c754-4174-9d0b-7402dc57a500,Kingdomino,"Build a kingdom with varied terrains on domino-shaped tiles in this fas
  113. namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; use Symfony\\Validator\Constraints\NotBlank; #[AsResource(driver: false)]

    final class BoardGameResource implements ResourceInterface { public function __construct( public ?string $id = null, #[NotBlank] public ?string $name = null, public ?string $shortDescription = null, ) { } public function getId(): string { return $this->id; } }
  114. #[AsResource(driver: false)] namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; use Symfony\\Validator\Constraints\NotBlank;

    final class BoardGameResource implements ResourceInterface { public function __construct( public ?string $id = null, #[NotBlank] public ?string $name = null, public ?string $shortDescription = null, ) { } public function getId(): string { return $this->id; } }
  115. use Sylius\Resource\Metadata\AsResource; #[AsResource(driver: false)] namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; use Sylius\Resource\Model\ResourceInterface; use Symfony\\Validator\Constraints\NotBlank;

    final class BoardGameResource implements ResourceInterface { public function __construct( public ?string $id = null, #[NotBlank] public ?string $name = null, public ?string $shortDescription = null, ) { } public function getId(): string { return $this->id; } }
  116. final class BoardGameResource implements ResourceInterface namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; use Sylius\Resource\Metadata\AsResource; use

    Sylius\Resource\Model\ResourceInterface; use Symfony\\Validator\Constraints\NotBlank; #[AsResource(driver: false)] { public function __construct( public ?string $id = null, #[NotBlank] public ?string $name = null, public ?string $shortDescription = null, ) { } public function getId(): string { return $this->id; } }
  117. use Sylius\Resource\Model\ResourceInterface; final class BoardGameResource implements ResourceInterface namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; use

    Sylius\Resource\Metadata\AsResource; use Symfony\\Validator\Constraints\NotBlank; #[AsResource(driver: false)] { public function __construct( public ?string $id = null, #[NotBlank] public ?string $name = null, public ?string $shortDescription = null, ) { } public function getId(): string { return $this->id; } }
  118. use Sylius\Resource\Model\ResourceInterface; final class BoardGameResource implements ResourceInterface public function getId():

    string { return $this->id; } namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; use Sylius\Resource\Metadata\AsResource; use Symfony\\Validator\Constraints\NotBlank; #[AsResource(driver: false)] { public function __construct( public ?string $id = null, #[NotBlank] public ?string $name = null, public ?string $shortDescription = null, ) { } }
  119. Browsing board games namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\AsResource; use

    Sylius\Resource\Model\ResourceInterface; use Symfony\\Validator\Constraints\NotBlank; #[AsResource(driver: false)] #[Index( grid: 'app_board_game' )] final class BoardGameResource implements ResourceInterface { // [...] }
  120. Browsing board games #[Index( grid: 'app_board_game' )] final class BoardGameResource

    implements ResourceInterface { namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; use Symfony\\Validator\Constraints\NotBlank; #[AsResource(driver: false)] // [...] }
  121. Browsing board games use Sylius\Resource\Metadata\Index; #[Index( grid: 'app_board_game' )] final

    class BoardGameResource implements ResourceInterface { namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; use Sylius\Resource\Metadata\AsResource; use Sylius\Resource\Model\ResourceInterface; use Symfony\\Validator\Constraints\NotBlank; #[AsResource(driver: false)] // [...] }
  122. // src/BoardGameBlog/Infrastructure/Sylius/Grid/BoardGameGrid.php final class BoardGameGrid extends AbstractGrid implements ResourceAwareGridInterface {

    public static function getName(): string { return 'app_board_game'; } public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->setProvider(BoardGameGridProvider::class) ->addField( StringField::create('name') ->setLabel('Name') ) ->addField( StringField::create('shortDescription') ->setLabel('Short Description'), ) ; } public function getResourceClass(): string { return BoardGameResource::class; } }
  123. public function getResourceClass(): string { return BoardGameResource::class; } // src/BoardGameBlog/Infrastructure/Sylius/Grid/BoardGameGrid.php

    final class BoardGameGrid extends AbstractGrid implements ResourceAwareGridInterface { public static function getName(): string { return 'app_board_game'; } public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->setProvider(BoardGameGridProvider::class) ->addField( StringField::create('name') ->setLabel('Name') ) ->addField( StringField::create('shortDescription') ->setLabel('Short Description'), ) ; } }
  124. ->setProvider(BoardGameGridProvider::class) // src/BoardGameBlog/Infrastructure/Sylius/Grid/BoardGameGrid.php final class BoardGameGrid extends AbstractGrid implements ResourceAwareGridInterface

    { public static function getName(): string { return 'app_board_game'; } public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->addField( StringField::create('name') ->setLabel('Name') ) ->addField( StringField::create('shortDescription') ->setLabel('Short Description'), ) ; } public function getResourceClass(): string { return BoardGameResource::class; } }
  125. namespace App\BoardGameBlog\Infrastructure\Sylius\Grid\DataProvider; // [...] use Sylius\Grid\Data\DataProviderInterface; final class BoardGameGridProvider implements

    DataProviderInterface { public function __construct(private readonly string $dataDir) {} public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; foreach ($this->getFileData() as $row) { [$id, $name, $shortDescription] = $row; $data[] = new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, ); } return new Pagerfanta(new ArrayAdapter($data)); }
  126. namespace App\BoardGameBlog\Infrastructure\Sylius\Grid\DataProvider; // [...] use Sylius\Grid\Data\DataProviderInterface; final class BoardGameGridProvider implements

    DataProviderInterface { public function __construct(private readonly string $dataDir) {} public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; foreach ($this->getFileData() as $row) { [$id, $name, $shortDescription] = $row; $data[] = new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, ); } return new Pagerfanta(new ArrayAdapter($data)); }
  127. use Sylius\Grid\Data\DataProviderInterface; final class BoardGameGridProvider implements DataProviderInterface namespace App\BoardGameBlog\Infrastructure\Sylius\Grid\DataProvider; //

    [...] { public function __construct(private readonly string $dataDir) {} public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; foreach ($this->getFileData() as $row) { [$id, $name, $shortDescription] = $row; $data[] = new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, ); } return new Pagerfanta(new ArrayAdapter($data)); }
  128. use Sylius\Grid\Data\DataProviderInterface; final class BoardGameGridProvider implements DataProviderInterface public function getData(Grid

    $grid, Parameters $parameters): Pagerfanta namespace App\BoardGameBlog\Infrastructure\Sylius\Grid\DataProvider; // [...] { public function __construct(private readonly string $dataDir) {} { $data = []; foreach ($this->getFileData() as $row) { [$id, $name, $shortDescription] = $row; $data[] = new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, ); } return new Pagerfanta(new ArrayAdapter($data)); }
  129. $data = []; namespace App\BoardGameBlog\Infrastructure\Sylius\Grid\DataProvider; // [...] use Sylius\Grid\Data\DataProviderInterface; final

    class BoardGameGridProvider implements DataProviderInterface { public function __construct(private readonly string $dataDir) {} public function getData(Grid $grid, Parameters $parameters): Pagerfanta { foreach ($this->getFileData() as $row) { [$id, $name, $shortDescription] = $row; $data[] = new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, ); } return new Pagerfanta(new ArrayAdapter($data)); }
  130. foreach ($this->getFileData() as $row) { namespace App\BoardGameBlog\Infrastructure\Sylius\Grid\DataProvider; // [...] use

    Sylius\Grid\Data\DataProviderInterface; final class BoardGameGridProvider implements DataProviderInterface { public function __construct(private readonly string $dataDir) {} public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; [$id, $name, $shortDescription] = $row; $data[] = new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, ); } return new Pagerfanta(new ArrayAdapter($data)); }
  131. [$id, $name, $shortDescription] = $row; namespace App\BoardGameBlog\Infrastructure\Sylius\Grid\DataProvider; // [...] use

    Sylius\Grid\Data\DataProviderInterface; final class BoardGameGridProvider implements DataProviderInterface { public function __construct(private readonly string $dataDir) {} public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; foreach ($this->getFileData() as $row) { $data[] = new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, ); } return new Pagerfanta(new ArrayAdapter($data)); }
  132. $data[] = new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription,

    ); namespace App\BoardGameBlog\Infrastructure\Sylius\Grid\DataProvider; // [...] use Sylius\Grid\Data\DataProviderInterface; final class BoardGameGridProvider implements DataProviderInterface { public function __construct(private readonly string $dataDir) {} public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; foreach ($this->getFileData() as $row) { [$id, $name, $shortDescription] = $row; } return new Pagerfanta(new ArrayAdapter($data)); }
  133. return new Pagerfanta(new ArrayAdapter($data)); namespace App\BoardGameBlog\Infrastructure\Sylius\Grid\DataProvider; // [...] use Sylius\Grid\Data\DataProviderInterface;

    final class BoardGameGridProvider implements DataProviderInterface { public function __construct(private readonly string $dataDir) {} public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; foreach ($this->getFileData() as $row) { [$id, $name, $shortDescription] = $row; $data[] = new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, ); } }
  134. namespace App\BoardGameBlog\Infrastructure\Sylius\Grid\DataProvider; // [...] use Sylius\Grid\Data\DataProviderInterface; final class BoardGameGridProvider implements

    DataProviderInterface { // [...] private function getFileData(): array { return array_map('str_getcsv', file($this->dataDir . '/board_games.csv')); } }
  135. private function getFileData(): array { return array_map('str_getcsv', file($this->dataDir . '/board_games.csv'));

    } namespace App\BoardGameBlog\Infrastructure\Sylius\Grid\DataProvider; // [...] use Sylius\Grid\Data\DataProviderInterface; final class BoardGameGridProvider implements DataProviderInterface { // [...] }
  136. Adding board games namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...] use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\CreateBoardGameProcessor; use

    App\BoardGameBlog\Infrastructure\Symfony\Form\Type\BoardGameType; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\Create; #[AsResource( driver: false, formType: BoardGameType::class), ] #[Index( grid: 'app_board_game' )] #[Create( processor: CreateBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  137. Adding board games formType: BoardGameType::class), namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...] use

    App\BoardGameBlog\Infrastructure\Sylius\State\Processor\CreateBoardGameProcessor; use App\BoardGameBlog\Infrastructure\Symfony\Form\Type\BoardGameType; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\Create; #[AsResource( driver: false, ] #[Index( grid: 'app_board_game' )] #[Create( processor: CreateBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  138. Adding board games use App\BoardGameBlog\Infrastructure\Symfony\Form\Type\BoardGameType; formType: BoardGameType::class), namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; //

    [...] use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\CreateBoardGameProcessor; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\Create; #[AsResource( driver: false, ] #[Index( grid: 'app_board_game' )] #[Create( processor: CreateBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  139. Adding board games processor: CreateBoardGameProcessor::class, namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...] use

    App\BoardGameBlog\Infrastructure\Sylius\State\Processor\CreateBoardGameProcessor; use App\BoardGameBlog\Infrastructure\Symfony\Form\Type\BoardGameType; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\Create; #[AsResource( driver: false, formType: BoardGameType::class), ] #[Index( grid: 'app_board_game' )] #[Create( )] final class BoardGameResource implements ResourceInterface { // [...] }
  140. Adding board games use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\CreateBoardGameProcessor; processor: CreateBoardGameProcessor::class, namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; //

    [...] use App\BoardGameBlog\Infrastructure\Symfony\Form\Type\BoardGameType; use Sylius\Resource\Metadata\Index; use Sylius\Resource\Metadata\Create; #[AsResource( driver: false, formType: BoardGameType::class), ] #[Index( grid: 'app_board_game' )] #[Create( )] final class BoardGameResource implements ResourceInterface { // [...] }
  141. namespace App\BoardGameBlog\Infrastructure\Sylius\State\Processor; // [...] use Sylius\Resource\State\ProcessorInterface; final class CreateBoardGameProcessor implements

    ProcessorInterface { public function __construct(private readonly string $dataDir) {} public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->createBoardGame($data); return null; } private function createBoardGame(BoardGameResource $boardGameResource): void { $handle = fopen($this->dataDir . '/board_games.csv', 'a'); fputcsv($handle, [(string) Uuid::v4(), $boardGameResource->name, $boardGameResource->shortDescription]); fclose($handle); } }
  142. final class CreateBoardGameProcessor implements ProcessorInterface namespace App\BoardGameBlog\Infrastructure\Sylius\State\Processor; // [...] use

    Sylius\Resource\State\ProcessorInterface; { public function __construct(private readonly string $dataDir) {} public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->createBoardGame($data); return null; } private function createBoardGame(BoardGameResource $boardGameResource): void { $handle = fopen($this->dataDir . '/board_games.csv', 'a'); fputcsv($handle, [(string) Uuid::v4(), $boardGameResource->name, $boardGameResource->shortDescription]); fclose($handle); } }
  143. use Sylius\Resource\State\ProcessorInterface; final class CreateBoardGameProcessor implements ProcessorInterface namespace App\BoardGameBlog\Infrastructure\Sylius\State\Processor; //

    [...] { public function __construct(private readonly string $dataDir) {} public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->createBoardGame($data); return null; } private function createBoardGame(BoardGameResource $boardGameResource): void { $handle = fopen($this->dataDir . '/board_games.csv', 'a'); fputcsv($handle, [(string) Uuid::v4(), $boardGameResource->name, $boardGameResource->shortDescription]); fclose($handle); } }
  144. use Sylius\Resource\State\ProcessorInterface; final class CreateBoardGameProcessor implements ProcessorInterface public function process(mixed

    $data, Operation $operation, Context $context): mixed namespace App\BoardGameBlog\Infrastructure\Sylius\State\Processor; // [...] { public function __construct(private readonly string $dataDir) {} { Assert::isInstanceOf($data, BoardGameResource::class); $this->createBoardGame($data); return null; } private function createBoardGame(BoardGameResource $boardGameResource): void { $handle = fopen($this->dataDir . '/board_games.csv', 'a'); fputcsv($handle, [(string) Uuid::v4(), $boardGameResource->name, $boardGameResource->shortDescription]); fclose($handle); } }
  145. Assert::isInstanceOf($data, BoardGameResource::class); namespace App\BoardGameBlog\Infrastructure\Sylius\State\Processor; // [...] use Sylius\Resource\State\ProcessorInterface; final class

    CreateBoardGameProcessor implements ProcessorInterface { public function __construct(private readonly string $dataDir) {} public function process(mixed $data, Operation $operation, Context $context): mixed { $this->createBoardGame($data); return null; } private function createBoardGame(BoardGameResource $boardGameResource): void { $handle = fopen($this->dataDir . '/board_games.csv', 'a'); fputcsv($handle, [(string) Uuid::v4(), $boardGameResource->name, $boardGameResource->shortDescription]); fclose($handle); } }
  146. $this->createBoardGame($data); namespace App\BoardGameBlog\Infrastructure\Sylius\State\Processor; // [...] use Sylius\Resource\State\ProcessorInterface; final class CreateBoardGameProcessor

    implements ProcessorInterface { public function __construct(private readonly string $dataDir) {} public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); return null; } private function createBoardGame(BoardGameResource $boardGameResource): void { $handle = fopen($this->dataDir . '/board_games.csv', 'a'); fputcsv($handle, [(string) Uuid::v4(), $boardGameResource->name, $boardGameResource->shortDescription]); fclose($handle); } }
  147. $this->createBoardGame($data); private function createBoardGame(BoardGameResource $boardGameResource): void { $handle = fopen($this->dataDir

    . '/board_games.csv', 'a'); fputcsv($handle, [(string) Uuid::v4(), $boardGameResource->name, $boardGameResource->shortDescription]); fclose($handle); } namespace App\BoardGameBlog\Infrastructure\Sylius\State\Processor; // [...] use Sylius\Resource\State\ProcessorInterface; final class CreateBoardGameProcessor implements ProcessorInterface { public function __construct(private readonly string $dataDir) {} public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); return null; } }
  148. $handle = fopen($this->dataDir . '/board_games.csv', 'a'); namespace App\BoardGameBlog\Infrastructure\Sylius\State\Processor; // [...]

    use Sylius\Resource\State\ProcessorInterface; final class CreateBoardGameProcessor implements ProcessorInterface { public function __construct(private readonly string $dataDir) {} public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->createBoardGame($data); return null; } private function createBoardGame(BoardGameResource $boardGameResource): void { fputcsv($handle, [(string) Uuid::v4(), $boardGameResource->name, $boardGameResource->shortDescription]); fclose($handle); } }
  149. fputcsv($handle, [(string) Uuid::v4(), $boardGameResource->name, $boardGameResource->shortDescription]); namespace App\BoardGameBlog\Infrastructure\Sylius\State\Processor; // [...] use

    Sylius\Resource\State\ProcessorInterface; final class CreateBoardGameProcessor implements ProcessorInterface { public function __construct(private readonly string $dataDir) {} public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->createBoardGame($data); return null; } private function createBoardGame(BoardGameResource $boardGameResource): void { $handle = fopen($this->dataDir . '/board_games.csv', 'a'); fclose($handle); } }
  150. fclose($handle); namespace App\BoardGameBlog\Infrastructure\Sylius\State\Processor; // [...] use Sylius\Resource\State\ProcessorInterface; final class CreateBoardGameProcessor

    implements ProcessorInterface { public function __construct(private readonly string $dataDir) {} public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->createBoardGame($data); return null; } private function createBoardGame(BoardGameResource $boardGameResource): void { $handle = fopen($this->dataDir . '/board_games.csv', 'a'); fputcsv($handle, [(string) Uuid::v4(), $boardGameResource->name, $boardGameResource->shortDescription]); } }
  151. return null; namespace App\BoardGameBlog\Infrastructure\Sylius\State\Processor; // [...] use Sylius\Resource\State\ProcessorInterface; final class

    CreateBoardGameProcessor implements ProcessorInterface { public function __construct(private readonly string $dataDir) {} public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->createBoardGame($data); } private function createBoardGame(BoardGameResource $boardGameResource): void { $handle = fopen($this->dataDir . '/board_games.csv', 'a'); fputcsv($handle, [(string) Uuid::v4(), $boardGameResource->name, $boardGameResource->shortDescription]); fclose($handle); } }
  152. Editing board games namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...] use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\UpdateBoardGameProcessor; use

    App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; use Sylius\Resource\Metadata\Update; // [...] #[Update( provider: BoardGameItemProvider::class, processor: UpdateBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  153. Editing board games #[Update( namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...] use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\UpdateBoardGameProcessor;

    use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; use Sylius\Resource\Metadata\Update; // [...] provider: BoardGameItemProvider::class, processor: UpdateBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  154. Editing board games use Sylius\Resource\Metadata\Update; #[Update( namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...]

    use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\UpdateBoardGameProcessor; use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; // [...] provider: BoardGameItemProvider::class, processor: UpdateBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  155. Editing board games provider: BoardGameItemProvider::class, namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...] use

    App\BoardGameBlog\Infrastructure\Sylius\State\Processor\UpdateBoardGameProcessor; use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; use Sylius\Resource\Metadata\Update; // [...] #[Update( processor: UpdateBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  156. Editing board games use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; provider: BoardGameItemProvider::class, namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; //

    [...] use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\UpdateBoardGameProcessor; use Sylius\Resource\Metadata\Update; // [...] #[Update( processor: UpdateBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  157. Editing board games processor: UpdateBoardGameProcessor::class, namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...] use

    App\BoardGameBlog\Infrastructure\Sylius\State\Processor\UpdateBoardGameProcessor; use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; use Sylius\Resource\Metadata\Update; // [...] #[Update( provider: BoardGameItemProvider::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  158. Editing board games use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\UpdateBoardGameProcessor; processor: UpdateBoardGameProcessor::class, namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; //

    [...] use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; use Sylius\Resource\Metadata\Update; // [...] #[Update( provider: BoardGameItemProvider::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  159. // [...] use Sylius\Resource\State\ProviderInterface; final class BoardGameItemProvider implements ProviderInterface {

    public function __construct(private readonly string $dataDir) {} public function provide(Operation $operation, Context $context): ?BoardGameResource { $request = $context->get(RequestOption::class)?->request(); Assert::notNull($request); $id = $request->attributes->get('id'); Assert::nullOrString($id); [$id, $name, $shortDescription] = $this->getFileData()[$id] ?? null; if (null === $id) { return null; } return new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, );
  160. final class BoardGameItemProvider implements ProviderInterface // [...] use Sylius\Resource\State\ProviderInterface; {

    public function __construct(private readonly string $dataDir) {} public function provide(Operation $operation, Context $context): ?BoardGameResource { $request = $context->get(RequestOption::class)?->request(); Assert::notNull($request); $id = $request->attributes->get('id'); Assert::nullOrString($id); [$id, $name, $shortDescription] = $this->getFileData()[$id] ?? null; if (null === $id) { return null; } return new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, );
  161. use Sylius\Resource\State\ProviderInterface; final class BoardGameItemProvider implements ProviderInterface // [...] {

    public function __construct(private readonly string $dataDir) {} public function provide(Operation $operation, Context $context): ?BoardGameResource { $request = $context->get(RequestOption::class)?->request(); Assert::notNull($request); $id = $request->attributes->get('id'); Assert::nullOrString($id); [$id, $name, $shortDescription] = $this->getFileData()[$id] ?? null; if (null === $id) { return null; } return new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, );
  162. public function provide(Operation $operation, Context $context): ?BoardGameResource // [...] use

    Sylius\Resource\State\ProviderInterface; final class BoardGameItemProvider implements ProviderInterface { public function __construct(private readonly string $dataDir) {} { $request = $context->get(RequestOption::class)?->request(); Assert::notNull($request); $id = $request->attributes->get('id'); Assert::nullOrString($id); [$id, $name, $shortDescription] = $this->getFileData()[$id] ?? null; if (null === $id) { return null; } return new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, );
  163. $request = $context->get(RequestOption::class)?->request(); Assert::notNull($request); // [...] use Sylius\Resource\State\ProviderInterface; final class

    BoardGameItemProvider implements ProviderInterface { public function __construct(private readonly string $dataDir) {} public function provide(Operation $operation, Context $context): ?BoardGameResource { $id = $request->attributes->get('id'); Assert::nullOrString($id); [$id, $name, $shortDescription] = $this->getFileData()[$id] ?? null; if (null === $id) { return null; } return new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, );
  164. $id = $request->attributes->get('id'); Assert::nullOrString($id); // [...] use Sylius\Resource\State\ProviderInterface; final class

    BoardGameItemProvider implements ProviderInterface { public function __construct(private readonly string $dataDir) {} public function provide(Operation $operation, Context $context): ?BoardGameResource { $request = $context->get(RequestOption::class)?->request(); Assert::notNull($request); [$id, $name, $shortDescription] = $this->getFileData()[$id] ?? null; if (null === $id) { return null; } return new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, );
  165. [$id, $name, $shortDescription] = $this->getFileData()[$id] ?? null; // [...] use

    Sylius\Resource\State\ProviderInterface; final class BoardGameItemProvider implements ProviderInterface { public function __construct(private readonly string $dataDir) {} public function provide(Operation $operation, Context $context): ?BoardGameResource { $request = $context->get(RequestOption::class)?->request(); Assert::notNull($request); $id = $request->attributes->get('id'); Assert::nullOrString($id); if (null === $id) { return null; } return new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, );
  166. if (null === $id) { return null; } // [...]

    use Sylius\Resource\State\ProviderInterface; final class BoardGameItemProvider implements ProviderInterface { public function __construct(private readonly string $dataDir) {} public function provide(Operation $operation, Context $context): ?BoardGameResource { $request = $context->get(RequestOption::class)?->request(); Assert::notNull($request); $id = $request->attributes->get('id'); Assert::nullOrString($id); [$id, $name, $shortDescription] = $this->getFileData()[$id] ?? null; return new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, );
  167. return new BoardGameResource( id: $id, name: $name, shortDescription: $shortDescription, );

    // [...] use Sylius\Resource\State\ProviderInterface; final class BoardGameItemProvider implements ProviderInterface { public function __construct(private readonly string $dataDir) {} public function provide(Operation $operation, Context $context): ?BoardGameResource { $request = $context->get(RequestOption::class)?->request(); Assert::notNull($request); $id = $request->attributes->get('id'); Assert::nullOrString($id); [$id, $name, $shortDescription] = $this->getFileData()[$id] ?? null; if (null === $id) { return null; }
  168. // [...] use Sylius\Resource\State\ProcessorInterface; final class UpdateBoardGameProcessor implements ProcessorInterface {

    public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->updateBoardGame($data); return null; } private function updateBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); $row = &$fileData[$boardGameResource->id]; $row[1] = $boardGameResource->name; $row[2] = $boardGameResource->shortDescription; $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data);
  169. final class UpdateBoardGameProcessor implements ProcessorInterface // [...] use Sylius\Resource\State\ProcessorInterface; {

    public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->updateBoardGame($data); return null; } private function updateBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); $row = &$fileData[$boardGameResource->id]; $row[1] = $boardGameResource->name; $row[2] = $boardGameResource->shortDescription; $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data);
  170. use Sylius\Resource\State\ProcessorInterface; final class UpdateBoardGameProcessor implements ProcessorInterface // [...] {

    public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->updateBoardGame($data); return null; } private function updateBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); $row = &$fileData[$boardGameResource->id]; $row[1] = $boardGameResource->name; $row[2] = $boardGameResource->shortDescription; $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data);
  171. public function process(mixed $data, Operation $operation, Context $context): mixed //

    [...] use Sylius\Resource\State\ProcessorInterface; final class UpdateBoardGameProcessor implements ProcessorInterface { { Assert::isInstanceOf($data, BoardGameResource::class); $this->updateBoardGame($data); return null; } private function updateBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); $row = &$fileData[$boardGameResource->id]; $row[1] = $boardGameResource->name; $row[2] = $boardGameResource->shortDescription; $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data);
  172. Assert::isInstanceOf($data, BoardGameResource::class); // [...] use Sylius\Resource\State\ProcessorInterface; final class UpdateBoardGameProcessor implements

    ProcessorInterface { public function process(mixed $data, Operation $operation, Context $context): mixed { $this->updateBoardGame($data); return null; } private function updateBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); $row = &$fileData[$boardGameResource->id]; $row[1] = $boardGameResource->name; $row[2] = $boardGameResource->shortDescription; $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data);
  173. $this->updateBoardGame($data); // [...] use Sylius\Resource\State\ProcessorInterface; final class UpdateBoardGameProcessor implements ProcessorInterface

    { public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); return null; } private function updateBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); $row = &$fileData[$boardGameResource->id]; $row[1] = $boardGameResource->name; $row[2] = $boardGameResource->shortDescription; $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data);
  174. $this->updateBoardGame($data); private function updateBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData();

    $row = &$fileData[$boardGameResource->id]; $row[1] = $boardGameResource->name; $row[2] = $boardGameResource->shortDescription; $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data); // [...] use Sylius\Resource\State\ProcessorInterface; final class UpdateBoardGameProcessor implements ProcessorInterface { public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); return null; }
  175. $fileData = $this->getFileData(); $row = &$fileData[$boardGameResource->id]; // [...] use Sylius\Resource\State\ProcessorInterface;

    final class UpdateBoardGameProcessor implements ProcessorInterface { public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->updateBoardGame($data); return null; } private function updateBoardGame(BoardGameResource $boardGameResource): void { $row[1] = $boardGameResource->name; $row[2] = $boardGameResource->shortDescription; $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data);
  176. $row[1] = $boardGameResource->name; $row[2] = $boardGameResource->shortDescription; // [...] use Sylius\Resource\State\ProcessorInterface;

    final class UpdateBoardGameProcessor implements ProcessorInterface { public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->updateBoardGame($data); return null; } private function updateBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); $row = &$fileData[$boardGameResource->id]; $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data);
  177. $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data)

    { fputcsv($handle, $data); // [...] use Sylius\Resource\State\ProcessorInterface; final class UpdateBoardGameProcessor implements ProcessorInterface { public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->updateBoardGame($data); return null; } private function updateBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); $row = &$fileData[$boardGameResource->id]; $row[1] = $boardGameResource->name; $row[2] = $boardGameResource->shortDescription;
  178. Deleting board games namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...] use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\DeleteBoardGameProcessor; use

    App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; use Sylius\Resource\Metadata\Delete; // [...] #[Delete( provider: BoardGameItemProvider::class, processor: DeleteBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  179. Deleting board games #[Delete( namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...] use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\DeleteBoardGameProcessor;

    use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; use Sylius\Resource\Metadata\Delete; // [...] provider: BoardGameItemProvider::class, processor: DeleteBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  180. Deleting board games use Sylius\Resource\Metadata\Delete; #[Delete( namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...]

    use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\DeleteBoardGameProcessor; use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; // [...] provider: BoardGameItemProvider::class, processor: DeleteBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  181. Deleting board games provider: BoardGameItemProvider::class, namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...] use

    App\BoardGameBlog\Infrastructure\Sylius\State\Processor\DeleteBoardGameProcessor; use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; use Sylius\Resource\Metadata\Delete; // [...] #[Delete( processor: DeleteBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  182. Deleting board games use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; provider: BoardGameItemProvider::class, namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; //

    [...] use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\DeleteBoardGameProcessor; use Sylius\Resource\Metadata\Delete; // [...] #[Delete( processor: DeleteBoardGameProcessor::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  183. Deleting board games processor: DeleteBoardGameProcessor::class, namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; // [...] use

    App\BoardGameBlog\Infrastructure\Sylius\State\Processor\DeleteBoardGameProcessor; use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; use Sylius\Resource\Metadata\Delete; // [...] #[Delete( provider: BoardGameItemProvider::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  184. Deleting board games use App\BoardGameBlog\Infrastructure\Sylius\State\Processor\DeleteBoardGameProcessor; processor: DeleteBoardGameProcessor::class, namespace App\BoardGameBlog\Infrastructure\Sylius\Resource; //

    [...] use App\BoardGameBlog\Infrastructure\Sylius\State\Provider\BoardGameItemProvider; use Sylius\Resource\Metadata\Delete; // [...] #[Delete( provider: BoardGameItemProvider::class, )] final class BoardGameResource implements ResourceInterface { // [...] }
  185. // [...] use Sylius\Resource\State\ProcessorInterface; final class DeleteBoardGameProcessor implements ProcessorInterface {

    public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->deleteBoardGame($data); return null; } private function deleteBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); unset($fileData[$boardGameResource->id]); $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data); } fclose($handle);
  186. final class DeleteBoardGameProcessor implements ProcessorInterface // [...] use Sylius\Resource\State\ProcessorInterface; {

    public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->deleteBoardGame($data); return null; } private function deleteBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); unset($fileData[$boardGameResource->id]); $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data); } fclose($handle);
  187. use Sylius\Resource\State\ProcessorInterface; final class DeleteBoardGameProcessor implements ProcessorInterface // [...] {

    public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->deleteBoardGame($data); return null; } private function deleteBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); unset($fileData[$boardGameResource->id]); $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data); } fclose($handle);
  188. public function process(mixed $data, Operation $operation, Context $context): mixed //

    [...] use Sylius\Resource\State\ProcessorInterface; final class DeleteBoardGameProcessor implements ProcessorInterface { { Assert::isInstanceOf($data, BoardGameResource::class); $this->deleteBoardGame($data); return null; } private function deleteBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); unset($fileData[$boardGameResource->id]); $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data); } fclose($handle);
  189. Assert::isInstanceOf($data, BoardGameResource::class); // [...] use Sylius\Resource\State\ProcessorInterface; final class DeleteBoardGameProcessor implements

    ProcessorInterface { public function process(mixed $data, Operation $operation, Context $context): mixed { $this->deleteBoardGame($data); return null; } private function deleteBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); unset($fileData[$boardGameResource->id]); $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data); } fclose($handle);
  190. $this->deleteBoardGame($data); // [...] use Sylius\Resource\State\ProcessorInterface; final class DeleteBoardGameProcessor implements ProcessorInterface

    { public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); return null; } private function deleteBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); unset($fileData[$boardGameResource->id]); $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data); } fclose($handle);
  191. $this->deleteBoardGame($data); private function deleteBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData();

    unset($fileData[$boardGameResource->id]); $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data); } fclose($handle); // [...] use Sylius\Resource\State\ProcessorInterface; final class DeleteBoardGameProcessor implements ProcessorInterface { public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); return null; }
  192. $fileData = $this->getFileData(); unset($fileData[$boardGameResource->id]); // [...] use Sylius\Resource\State\ProcessorInterface; final class

    DeleteBoardGameProcessor implements ProcessorInterface { public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->deleteBoardGame($data); return null; } private function deleteBoardGame(BoardGameResource $boardGameResource): void { $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data) { fputcsv($handle, $data); } fclose($handle);
  193. $handle = fopen($this->dataDir . '/board_games.csv', 'wb'); foreach ($fileData as $data)

    { fputcsv($handle, $data); } fclose($handle); // [...] use Sylius\Resource\State\ProcessorInterface; final class DeleteBoardGameProcessor implements ProcessorInterface { public function process(mixed $data, Operation $operation, Context $context): mixed { Assert::isInstanceOf($data, BoardGameResource::class); $this->deleteBoardGame($data); return null; } private function deleteBoardGame(BoardGameResource $boardGameResource): void { $fileData = $this->getFileData(); unset($fileData[$boardGameResource->id]);
  194. Sorting board games final class BoardGameGrid extends AbstractGrid implements ResourceAwareGridInterface

    { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->setProvider(BoardGameGridProvider::class) ->orderBy('name', 'asc') ->addField( StringField::create('name') ->setLabel('Name') ->setSortable(true), ) // [...] ; } // [...] }
  195. Sorting board games ->addField( StringField::create('name') ->setLabel('Name') ->setSortable(true), ) final class

    BoardGameGrid extends AbstractGrid implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->setProvider(BoardGameGridProvider::class) ->orderBy('name', 'asc') // [...] ; } // [...] }
  196. Sorting board games ->setSortable(true), final class BoardGameGrid extends AbstractGrid implements

    ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->setProvider(BoardGameGridProvider::class) ->orderBy('name', 'asc') ->addField( StringField::create('name') ->setLabel('Name') ) // [...] ; } // [...] }
  197. Sorting board games ->orderBy('name', 'asc') final class BoardGameGrid extends AbstractGrid

    implements ResourceAwareGridInterface { // [...] public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->setProvider(BoardGameGridProvider::class) ->addField( StringField::create('name') ->setLabel('Name') ->setSortable(true), ) // [...] ; } // [...] }
  198. final class BoardGameGridProvider implements DataProviderInterface { public function getData(Grid $grid,

    Parameters $parameters): Pagerfanta { $data = []; $fileData = $this->getFileData(); $sorting = $parameters->get('sorting') ?? []; $fileData = $this->sortData($fileData, $sorting); // [...] } private function sortData(array $data, array $sorting): array { if ('asc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameAsc']); } if ('desc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameDesc']); } return $data;
  199. $sorting = $parameters->get('sorting') ?? []; final class BoardGameGridProvider implements DataProviderInterface

    { public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; $fileData = $this->getFileData(); $fileData = $this->sortData($fileData, $sorting); // [...] } private function sortData(array $data, array $sorting): array { if ('asc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameAsc']); } if ('desc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameDesc']); } return $data;
  200. $fileData = $this->sortData($fileData, $sorting); final class BoardGameGridProvider implements DataProviderInterface {

    public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; $fileData = $this->getFileData(); $sorting = $parameters->get('sorting') ?? []; // [...] } private function sortData(array $data, array $sorting): array { if ('asc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameAsc']); } if ('desc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameDesc']); } return $data;
  201. $fileData = $this->sortData($fileData, $sorting); private function sortData(array $data, array $sorting):

    array { if ('asc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameAsc']); } if ('desc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameDesc']); } return $data; final class BoardGameGridProvider implements DataProviderInterface { public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; $fileData = $this->getFileData(); $sorting = $parameters->get('sorting') ?? []; // [...] }
  202. final class BoardGameGridProvider implements DataProviderInterface { // [...] private function

    sortData(array $data, array $sorting): array { if ('asc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameAsc']); } if ('desc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameDesc']); } return $data; } private function sortByNameAsc($a, $b): int { return strcmp($a[1], $b[1]); } private function sortByNameDesc($a, $b): int { return strcmp($b[1], $a[1]); }
  203. if ('asc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameAsc']);

    } final class BoardGameGridProvider implements DataProviderInterface { // [...] private function sortData(array $data, array $sorting): array { if ('desc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameDesc']); } return $data; } private function sortByNameAsc($a, $b): int { return strcmp($a[1], $b[1]); } private function sortByNameDesc($a, $b): int { return strcmp($b[1], $a[1]); }
  204. if ('asc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameAsc']);

    } private function sortByNameAsc($a, $b): int { return strcmp($a[1], $b[1]); } final class BoardGameGridProvider implements DataProviderInterface { // [...] private function sortData(array $data, array $sorting): array { if ('desc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameDesc']); } return $data; } private function sortByNameDesc($a, $b): int { return strcmp($b[1], $a[1]); }
  205. if ('desc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameDesc']);

    } final class BoardGameGridProvider implements DataProviderInterface { // [...] private function sortData(array $data, array $sorting): array { if ('asc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameAsc']); } return $data; } private function sortByNameAsc($a, $b): int { return strcmp($a[1], $b[1]); } private function sortByNameDesc($a, $b): int { return strcmp($b[1], $a[1]); }
  206. if ('desc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameDesc']);

    } private function sortByNameDesc($a, $b): int { return strcmp($b[1], $a[1]); } final class BoardGameGridProvider implements DataProviderInterface { // [...] private function sortData(array $data, array $sorting): array { if ('asc' === ($sorting['name'] ?? null)) { usort($data, [$this, 'sortByNameAsc']); } return $data; } private function sortByNameAsc($a, $b): int { return strcmp($a[1], $b[1]); }
  207. final class BoardGameGridProvider implements DataProviderInterface { public function getData(Grid $grid,

    Parameters $parameters): Pagerfanta { $data = []; $fileData = $this->getFileData(); $sorting = $parameters->get('sorting') ?? []; $fileData = $this->sortData($fileData, $sorting); // [...] } // [...] }
  208. $sorting = $parameters->get('sorting') ?? []; final class BoardGameGridProvider implements DataProviderInterface

    { public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; $fileData = $this->getFileData(); $fileData = $this->sortData($fileData, $sorting); // [...] } // [...] }
  209. $sorting = $parameters->get('sorting') ?? $grid->getSorting(); final class BoardGameGridProvider implements DataProviderInterface

    { public function getData(Grid $grid, Parameters $parameters): Pagerfanta { $data = []; $fileData = $this->getFileData(); $fileData = $this->sortData($fileData, $sorting); // [...] } // [...] }
  210. What’s next? Stable release Reunite bundle & component into same

    package Remove Kernel events (WIP thx to Vasilvestre)
  211. What’s next? Stable release Reunite bundle & component into same

    package Remove Kernel events (WIP thx to Vasilvestre) PHP configuration
  212. Resource configurator // config/package/sylius_resource.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book; use App\Entity\Subscription;

    use Sylius\Component\Resource\Metadata\ResourceMetadata; use Sylius\Component\Loader\Configuration\ResourceConfigurator; return static function (ResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withResource((new ResourceMetadata(Book::class)) ->withRoutePrefix('admin') ->withOperations([ new Index(), new Create(), new Update(), new Delete(), ]) ) ->withResource((new ResourceMetadata(Subscription::class)) ->withOperations([ new Index(), ]) )
  213. Resource configurator return static function (ResourceConfigurator $resourceConfigurator): void { //

    config/package/sylius_resource.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book; use App\Entity\Subscription; use Sylius\Component\Resource\Metadata\ResourceMetadata; use Sylius\Component\Loader\Configuration\ResourceConfigurator; $resourceConfigurator ->withResource((new ResourceMetadata(Book::class)) ->withRoutePrefix('admin') ->withOperations([ new Index(), new Create(), new Update(), new Delete(), ]) ) ->withResource((new ResourceMetadata(Subscription::class)) ->withOperations([ new Index(), ]) )
  214. Resource configurator use Sylius\Component\Loader\Configuration\ResourceConfigurator; return static function (ResourceConfigurator $resourceConfigurator): void

    { // config/package/sylius_resource.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book; use App\Entity\Subscription; use Sylius\Component\Resource\Metadata\ResourceMetadata; $resourceConfigurator ->withResource((new ResourceMetadata(Book::class)) ->withRoutePrefix('admin') ->withOperations([ new Index(), new Create(), new Update(), new Delete(), ]) ) ->withResource((new ResourceMetadata(Subscription::class)) ->withOperations([ new Index(), ]) )
  215. Resource configurator ->withResource((new ResourceMetadata(Book::class)) // config/package/sylius_resource.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book;

    use App\Entity\Subscription; use Sylius\Component\Resource\Metadata\ResourceMetadata; use Sylius\Component\Loader\Configuration\ResourceConfigurator; return static function (ResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withRoutePrefix('admin') ->withOperations([ new Index(), new Create(), new Update(), new Delete(), ]) ) ->withResource((new ResourceMetadata(Subscription::class)) ->withOperations([ new Index(), ]) )
  216. Resource configurator use Sylius\Component\Resource\Metadata\ResourceMetadata; ->withResource((new ResourceMetadata(Book::class)) // config/package/sylius_resource.php namespace Symfony\Component\DependencyInjection\Loader\Configurator;

    use App\Entity\Book; use App\Entity\Subscription; use Sylius\Component\Loader\Configuration\ResourceConfigurator; return static function (ResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withRoutePrefix('admin') ->withOperations([ new Index(), new Create(), new Update(), new Delete(), ]) ) ->withResource((new ResourceMetadata(Subscription::class)) ->withOperations([ new Index(), ]) )
  217. Resource configurator ->withRoutePrefix('admin') // config/package/sylius_resource.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book; use

    App\Entity\Subscription; use Sylius\Component\Resource\Metadata\ResourceMetadata; use Sylius\Component\Loader\Configuration\ResourceConfigurator; return static function (ResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withResource((new ResourceMetadata(Book::class)) ->withOperations([ new Index(), new Create(), new Update(), new Delete(), ]) ) ->withResource((new ResourceMetadata(Subscription::class)) ->withOperations([ new Index(), ]) )
  218. Resource configurator ->withOperations([ new Index(), new Create(), new Update(), new

    Delete(), ]) // config/package/sylius_resource.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book; use App\Entity\Subscription; use Sylius\Component\Resource\Metadata\ResourceMetadata; use Sylius\Component\Loader\Configuration\ResourceConfigurator; return static function (ResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withResource((new ResourceMetadata(Book::class)) ->withRoutePrefix('admin') ) ->withResource((new ResourceMetadata(Subscription::class)) ->withOperations([ new Index(), ]) )
  219. Resource configurator ->withResource((new ResourceMetadata(Subscription::class)) // config/package/sylius_resource.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book;

    use App\Entity\Subscription; use Sylius\Component\Resource\Metadata\ResourceMetadata; use Sylius\Component\Loader\Configuration\ResourceConfigurator; return static function (ResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withResource((new ResourceMetadata(Book::class)) ->withRoutePrefix('admin') ->withOperations([ new Index(), new Create(), new Update(), new Delete(), ]) ) ->withOperations([ new Index(), ]) )
  220. Resource configurator ->withOperations([ new Index(), ]) // config/package/sylius_resource.php namespace Symfony\Component\DependencyInjection\Loader\Configurator;

    use App\Entity\Book; use App\Entity\Subscription; use Sylius\Component\Resource\Metadata\ResourceMetadata; use Sylius\Component\Loader\Configuration\ResourceConfigurator; return static function (ResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withResource((new ResourceMetadata(Book::class)) ->withRoutePrefix('admin') ->withOperations([ new Index(), new Create(), new Update(), new Delete(), ]) ) ->withResource((new ResourceMetadata(Subscription::class)) )
  221. // config/api_platform/resources.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book; use App\Entity\Subscription; use ApiPlatform\Metadata\ApiResource;

    use ApiPlatorm\Loader\Configuration\ApiResourceConfigurator; return static function (ApiResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withResource((new ApiResource(Book::class)) ->withRoutePrefix('admin') ->withOperations([ new GetCollection(), new Post(), new Put(), new Delete(), ]) ) ; };
  222. return static function (ApiResourceConfigurator $resourceConfigurator): void { // config/api_platform/resources.php namespace

    Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book; use App\Entity\Subscription; use ApiPlatform\Metadata\ApiResource; use ApiPlatorm\Loader\Configuration\ApiResourceConfigurator; $resourceConfigurator ->withResource((new ApiResource(Book::class)) ->withRoutePrefix('admin') ->withOperations([ new GetCollection(), new Post(), new Put(), new Delete(), ]) ) ; };
  223. use ApiPlatorm\Loader\Configuration\ApiResourceConfigurator; return static function (ApiResourceConfigurator $resourceConfigurator): void { //

    config/api_platform/resources.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book; use App\Entity\Subscription; use ApiPlatform\Metadata\ApiResource; $resourceConfigurator ->withResource((new ApiResource(Book::class)) ->withRoutePrefix('admin') ->withOperations([ new GetCollection(), new Post(), new Put(), new Delete(), ]) ) ; };
  224. ->withResource((new ApiResource(Book::class)) // config/api_platform/resources.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book; use App\Entity\Subscription;

    use ApiPlatform\Metadata\ApiResource; use ApiPlatorm\Loader\Configuration\ApiResourceConfigurator; return static function (ApiResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withRoutePrefix('admin') ->withOperations([ new GetCollection(), new Post(), new Put(), new Delete(), ]) ) ; };
  225. use ApiPlatform\Metadata\ApiResource; ->withResource((new ApiResource(Book::class)) // config/api_platform/resources.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book;

    use App\Entity\Subscription; use ApiPlatorm\Loader\Configuration\ApiResourceConfigurator; return static function (ApiResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withRoutePrefix('admin') ->withOperations([ new GetCollection(), new Post(), new Put(), new Delete(), ]) ) ; };
  226. ->withRoutePrefix('admin') // config/api_platform/resources.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book; use App\Entity\Subscription; use

    ApiPlatform\Metadata\ApiResource; use ApiPlatorm\Loader\Configuration\ApiResourceConfigurator; return static function (ApiResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withResource((new ApiResource(Book::class)) ->withOperations([ new GetCollection(), new Post(), new Put(), new Delete(), ]) ) ; };
  227. ->withOperations([ new GetCollection(), new Post(), new Put(), new Delete(), ])

    // config/api_platform/resources.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Entity\Book; use App\Entity\Subscription; use ApiPlatform\Metadata\ApiResource; use ApiPlatorm\Loader\Configuration\ApiResourceConfigurator; return static function (ApiResourceConfigurator $resourceConfigurator): void { $resourceConfigurator ->withResource((new ApiResource(Book::class)) ->withRoutePrefix('admin') ) ; };