Création d'une resource avec Sylius 2

Une fois le schéma de données spécifié, nous allons maintenant créer une première ressource simple.

Pour celles et ceux qui le souhaitent, vous pouvez utiliser MakerBundle pour générer vos entités :​

cd apps/sylius && symfony composer require --dev symfony/maker-bundle

Cependant, dans ce tutoriel, nous allons tout faire manuellement, sans utiliser cet outil.​

Entité Constructor

erDiagram
    Constructor {
        int id PK
        string logo
        string name
    }
erDiagram
    Constructor {
        int id PK
        string logo
        string name
    }

Nous allons commencer par créer l'entité la plus simple : Constructor.
Avant cela, nous allons d'abord définir une interface.

Ce n'est pas obligatoire, mais si vous développez un plugin, il est recommandé d'ajouter une interface. Cela permet aux utilisateurs de l'étendre selon leurs besoins.

<?php

declare(strict_types=1);

namespace App\Entity\Constructor;

use Sylius\Component\Resource\Model\ResourceInterface;
use Sylius\Resource\Model\TimestampableInterface;

interface ConstructorInterface extends ResourceInterface, TimestampableInterface
{
    public function getName(): ?string;

    public function setName(?string $name): void;

    public function getLogo(): ?string;

    public function setLogo(?string $logo): void;
}

Explications

Nous étendons deux interfaces :

  • ResourceInterface : permet d'implémenter les fonctionnalités d'une ressource Sylius.
  • TimestampableInterface : ajoute automatiquement les méthodes qui nous permettront de gérer des champs created_at et updated_at.

Si vous ne souhaitez pas avoir d'informations de date de création et/ou de modification de votre entité, l'utilisation du TimestampableInterface n'est pas nécessaire.

Penchons nous maintenant sur la classe de l'entité :

<?php

declare(strict_types=1);

namespace App\Entity\Game;

use App\Repository\Game\ConstructorRepository;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Sylius\Component\Resource\Model\TimestampableTrait;
use Sylius\Resource\Metadata\AsResource;
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Entity(repositoryClass: ConstructorRepository::class)]
#[ORM\Table(name: 'app_constructor')]
#[AsResource]
class Constructor implements ConstructorInterface
{
    use TimestampableTrait;

    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[Assert\NotBlank()]
    #[ORM\Column(length: 255, nullable: true)]
    private ?string $name = null;

    #[ORM\Column(length: 255, nullable: true)]
    private ?string $logo = null;

    /**
     * @var ?DateTimeInterface
     */
    #[ORM\Column(name: 'created_at', type: 'datetime_immutable')]
    #[Gedmo\Timestampable(on: 'create')]
    protected $createdAt;

    /**
     * @var ?DateTimeInterface
     */
    #[ORM\Column(name: 'updated_at', type: 'datetime')]
    #[Gedmo\Timestampable(on: 'update')]
    protected $updatedAt;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(?string $name): void
    {
        $this->name = $name;
    }

    public function getLogo(): ?string
    {
        return $this->logo;
    }

    public function setLogo(?string $logo): void
    {
        $this->logo = $logo;
    }
}

Explications

  • Nous utilisons l'attribut PHP 8 AsResource pour signaler à Sylius que notre classe doit être considérée comme une ressource.
  • Nous déclarons dès maintenant le repository que nous allons utiliser pour cette entité : ConstructorRepository.
  • Nous utilisons TimestampableTrait qui nous permet d'avoir les méthodes nécessaires pour les champs created_at et updated_at.​

​Si vous ne souhaitez pas avoir d'informations de date de création et/ou de modification de votre entité, l'utilisation du TimestampableTrait n'est pas nécessaire.

Repository pour l'entité Constructor

Avant de générer la migration. Je vous propose de créer et d'expliquer rapidement la création du repository.

Comme précédemment, nous nous considérons dans un contexte de plugin où nos classes sont surchargeables par des projets tiers. Nous allons donc créer une interface pour notre repository. Pour le moment, étant donné que nous n'avons pas de méthode particulière, nous n'aurons qu'une interface vide.

Attention à bien étendrela ​RepositoryInterface proposé par Sylius Resource.

<?php

declare(strict_types=1);

namespace App\Repository\Game;

use Sylius\Component\Resource\Repository\RepositoryInterface;

interface ConstructorRepositoryInterface extends RepositoryInterface
{
}

Puis le code de notre repository en lui même.

<?php

declare(strict_types=1);

namespace App\Repository\Game;

use App\Entity\Game\Constructor;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\ResourceRepositoryTrait;

class ConstructorRepository extends ServiceEntityRepository implements ConstructorRepositoryInterface
{
    use ResourceRepositoryTrait;

    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Constructor::class);
    }
}

Explications

  • Nous étendons ​ServiceEntityRepository proposé par Doctrine
  • Nous utilisons le ResourceRepositoryTrait qui nous permet d'implémenter les méthodes de la RepositoryInterface étendue par notre ConstructorRepositoryInterface
  • Dans la méthode __construct, nous appelons la méthode du parent avec la classe de notre entité en second paramètre Constructor::class

Génération de la migration doctrine

Nous pouvons maintenant générer la migration :

± symfony console doctrine:migrations:diff --namespace="App"
 Generated new migration class to "/Users/mhuran/Sites/sylius-tutorials/apps/sylius/migrations/Version20250118134804.php"

 To run just this migration for testing purposes, you can use migrations:execute --up 'App\\Version20250118134804'

 To revert the migration you can use migrations:execute --down 'App\\Version20250118134804'

Puis, nous appliquons cette migration :

± symfony console doctrine:migrations:migrate

 WARNING! You are about to execute a migration in database "sylius_dev" that could result in schema changes and data loss. Are you sure you wish to continue? (yes/no) [yes]:
 > yes

[notice] Migrating up to App\Version20250118134804
[notice] finished in 25.2ms, used 100.5M memory, 1 migrations executed, 1 sql queries

 [OK] Successfully migrated to version: App\Version20250118134804

Utiliser le debug pour vérifier notre ressource

Pour vérifier si notre ressource Constructor est bien enregistrée dans Sylius, nous pouvons utiliser la commande suivante :​

± sf console sylius:debug:resource
 ---------------------------------------------
  Alias
 ---------------------------------------------
  app.constructor
  sylius.address
  sylius.address_log_entry
  sylius.adjustment
...
 ---------------------------------------------

Notre resource app.constructor est bien là ! Vous pouvez relancer la commande avec le nom de la ressource en paramètre pour afficher ses informations complètes.

± sf console sylius:debug:resource app.constructor

Configuration
-------------

 ----------------------- ---------------------------------------------------------------------------------
  Option                  Value
 ----------------------- ---------------------------------------------------------------------------------
  name                    "constructor"
  applicationName         "app"
  driver                  "doctrine/orm"
  stateMachineComponent   null
  templatesNamespace      null
  classes                 [
                            "model" => "App\Entity\Constructor\Constructor",
                            "controller" => "Sylius\Bundle\ResourceBundle\Controller\ResourceController",
                            "factory" => "Sylius\Resource\Factory\Factory",
                            "form" => "Sylius\Bundle\ResourceBundle\Form\Type\DefaultResourceType"
                          ]
 ----------------------- ---------------------------------------------------------------------------------

New Resource Metadata
---------------------

 ------------------------ --------------------------------------
  Option                   Value
 ------------------------ --------------------------------------
  alias                    "app.constructor"
  section                  null
  formType                 null
  templatesDir             null
  routePrefix              null
  name                     "constructor"
  pluralName               null
  applicationName          "app"
  identifier               null
  normalizationContext     null
  denormalizationContext   null
  validationContext        null
  class                    "App\Entity\Constructor\Constructor"
  driver                   null
  vars                     null
 ------------------------ --------------------------------------

 ------------------------ --------------------------------------
  Option                   Value
 ------------------------ --------------------------------------
  alias                    "app.constructor"
  section                  null
  formType                 null
  templatesDir             null
  routePrefix              null
  name                     "constructor"
  pluralName               null
  applicationName          "app"
  identifier               null
  normalizationContext     null
  denormalizationContext   null
  validationContext        null
  class                    "App\Entity\Constructor\Constructor"
  driver                   null
  vars                     null
 ------------------------ --------------------------------------

New operations
--------------


 [INFO] This resource has no defined operations.


---

Les services crées par Sylius automatiquement

Sylius va gérer pour nous automatiquement tout un tas de services bien pratiques ! Nous aurons l'occasion de nous en servir très prochainement et aussi d'en personnaliser par la suite ☺️

± sf console debug:container | grep -i constructor
  App\Repository\Game\ConstructorRepository                                                              App\Repository\Game\ConstructorRepository
  App\Repository\Game\ConstructorRepositoryInterface                                                     alias for "App\Repository\Game\ConstructorRepository"
  Doctrine\Common\Collections\Selectable $constructorRepository                                          alias for "app.repository.constructor"
  Doctrine\Common\Persistence\ObjectManager $constructorManager                                          alias for "doctrine.orm.default_entity_manager"
  Doctrine\ORM\EntityManagerInterface $constructorManager                                                alias for "doctrine.orm.default_entity_manager"
  Doctrine\ORM\EntityRepository $constructorRepository                                                   alias for "app.repository.constructor"
  Doctrine\Persistence\ObjectManager $constructorManager                                                 alias for "doctrine.orm.default_entity_manager"
  Doctrine\Persistence\ObjectRepository $constructorRepository                                           alias for "app.repository.constructor"
  Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository $constructorRepository                      alias for "app.repository.constructor"
  Sylius\Component\Resource\Factory\FactoryInterface $constructorFactory                                 alias for "app.factory.constructor"
  Sylius\Component\Resource\Repository\RepositoryInterface $constructorRepository                        alias for "app.repository.constructor"
  Sylius\Resource\Doctrine\Persistence\RepositoryInterface $constructorRepository                        alias for "app.repository.constructor"
  Sylius\Resource\Factory\Factory $constructorFactory                                                    alias for "app.factory.constructor"
  Sylius\Resource\Factory\FactoryInterface $constructorFactory                                           alias for "app.factory.constructor"
  app.controller.constructor                                                                             Sylius\Bundle\ResourceBundle\Controller\ResourceController
  app.controller_state_machine.constructor                                                               alias for "sylius.resource_controller.state_machine"
  app.factory.constructor                                                                                Sylius\Resource\Factory\Factory
  app.manager.constructor                                                                                alias for "doctrine.orm.default_entity_manager"
  app.repository.constructor                                                                             Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository

Explication des paramètres d'une ressource

La classe AsResource propose tout un tas de paramètres lors de son utilisation.

Pour le moment, nous n'avons rien mis, mais voici toutes les variables possibles :

<?php
// ...

final class AsResource
{
    public function __construct(
        private ?string $alias = null,
        private ?string $section = null,
        private ?string $formType = null,
        private ?string $templatesDir = null,
        private ?string $routePrefix = null,
        private ?string $name = null,
        private ?string $pluralName = null,
        private ?string $applicationName = null,
        private ?string $identifier = null,
        private ?array $normalizationContext = null,
        private ?array $denormalizationContext = null,
        private ?array $validationContext = null,
        private ?string $class = null,
        private string|false|null $driver = null,
        private ?array $vars = null,
        private ?array $operations = null,
    ) {
    }
]
// ...

Voyons ensemble quelques-uns de ces paramètres :

Dans Sylius, une ressource est définie avec plusieurs options qui influencent son comportement et son affichage dans les templates Twig.

  • alias → Définition de la ressource
    Cet alias permet d’identifier la ressource dans l’application. De la forme <applicationName>.<name>
    Dans le cadre d'un plugin, cela pourrait être maximehuran_games_plugin.constructor ou on pourrait mettre app.constructeur

    ⚠️ La partie <name> va servir à Sylius pour définir tous les services automatiquement générés par Sylius pour ses ressources comme vu précédemment

    En mettant app.constructeur cela renommerait tous les services. Si vous voulez changer ce nom, soyez prudents !

#[AsResource(alias: 'app.constructeur')]
  • name → Nom de la variable dans les templates
    Cette option permet de renommer la variable utilisée dans les templates Twig.
    Par exemple, dans les templates Twig, la variable constructor sera remplacée par constructeur si l’on définit :​​
#[AsResource(name: 'constructeur')]
  • pluralName → Nom au pluriel dans les templates
    De la même manière, cette option définit le nom utilisé pour les collections d’éléments dans les templates.
    Par exemple :​​
#[AsResource(pluralName: 'constructeurs')]
  • Modification de l’identifiant de la ressource
    Par défaut, l’identifiant d’une ressource est son id. Il est possible de le modifier avec l’attribut identifier.
    Par exemple, si vous avez une entité avec un champ code unique, vous pouvez l'utiliser :
#[AsResource(identifier: 'code')]

Le but n'est pas de tout détailler, mais de découvrir au fur et à mesure les possibilités offertes par tous ces paramètres.

Dans le prochain tutoriel. Nous aborderons la génération des fixtures pour notre entité Constructor. Cela permettra de bien appréhender la création de celle-ci avec des champs simples dans un premier temps (Notre entité étant très basique). Ensuite, nous aurons du concret, car viendra le temps des affichages dans le panneau d'administration.

⚠️ La suite de la formation est en cours de rédaction