Symfony 6 / Sonata 5 : Installation de SonataClassification.

Nous avons vu dans les article précédents comment initialiser un projet Symfony 6 + Sonata 5 avec les médias et les utilisateurs dans un admin sécurisé. Nous avons vu ensuite comment gérer les localisations de l’utilisateur.
Nous allons voir maintenant comment installer et gérer les classifications.

On procède à l’installation de la dernière version en date.

php8.1 composer.phar require  sonata-project/classification-bundle:5.x-dev

Visiblement, nous avons une erreur de configuration.

Unrecognized option "media" under "sonata_classification.class". Available   
!!    options are "category", "collection", "context", "tag".      

On retire la section médias de la configuration sonata_classification.yaml

sonata_classification:
    class:
        category: App\Entity\SonataClassificationCategory
        collection: App\Entity\SonataClassificationCollection
        context: App\Entity\SonataClassificationContext
        tag: App\Entity\SonataClassificationTag
        #media: App\Entity\SonataMediaMedia

Nous avons ensuite une nouvelle erreur.

PHP Fatal error:  Type of App\Entity\SonataClassificationCategory::$children must be Doctrine\Common\Collections\Collection (as in class Sonata\ClassificationBundle\Model\Category) in /src/Entity/SonataClassificationCategory.php on line 18

Ce n’est pas grave, on va tout configurer pour que ça passe du premier coup.
On va déjà implémenter nos entity dans un répertoire autre que src/app histoire de ne pas polluer notre application avec les surcharges de nos bundles. Tout comme UserBundle et MediaBundle, on met ça dans src/Application/Sonata/ClassificationBundle/Entity et on va créer nos 4 fichiers.

src/Application/Sonata/ClassificationBundle/Entity/SonataClassificationCategory.php

<?php
declare(strict_types=1);

namespace App\Application\Sonata\ClassificationBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Sonata\ClassificationBundle\Entity\BaseCategory;

/**
 * @ORM\Entity
 * @ORM\Table(name="classification__category")
 */
class SonataClassificationCategory extends BaseCategory
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    protected $id;
    
    public function getId(): ?int
    {
        return $this->id;
    }
}
?>


src/Application/Sonata/ClassificationBundle/Entity/SonataClassificationCollection.php

<?php 
declare(strict_types=1);

namespace App\Application\Sonata\ClassificationBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Sonata\ClassificationBundle\Entity\BaseCollection;

/**
 * @ORM\Entity
 * @ORM\Table(name="classification__collection")
 */
class SonataClassificationCollection extends BaseCollection
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    protected $id;
    
    public function getId(): ?int
    {
        return $this->id;
    }
}


?>

src/Application/Sonata/ClassificationBundle/Entity/SonataClassificationContext.php

<?php 
declare(strict_types=1);

namespace App\Application\Sonata\ClassificationBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Sonata\ClassificationBundle\Entity\BaseContext;

/**
 * @ORM\Entity
 * @ORM\Table(name="classification__context")
 */
class SonataClassificationContext extends BaseContext
{
    /**
     * @ORM\Id
     * @ORM\Column(name="id", type="string", length=64, nullable=false)
     */
     protected ?string $id = null;
    
    public function getId(): ?string
    {
        return $this->id;
    }
}

?>

src/Application/Sonata/ClassificationBundle/Entity/SonataClassificationTag.php

<?php
declare(strict_types=1);

namespace App\Application\Sonata\ClassificationBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Sonata\ClassificationBundle\Entity\BaseTag;

/**
 * @ORM\Entity
 * @ORM\Table(name="classification__tag")
 */
class SonataClassificationTag extends BaseTag
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    protected $id;
    
    public function getId(): ?int
    {
        return $this->id;
    }
}

Ensuite, il nous faut donner les path de nos classes dans config/packages/sonata_classification.yaml

sonata_classification:
    class:
        category: App\Application\Sonata\ClassificationBundle\Entity\SonataClassificationCategory
        collection: App\Application\Sonata\ClassificationBundle\Entity\SonataClassificationCollection
        context: App\Application\Sonata\ClassificationBundle\Entity\SonataClassificationContext
        tag: App\Application\Sonata\ClassificationBundle\Entity\SonataClassificationTag
   

Il faut aussi que l’on ajoute le path de la classe catégorie dans la définition des médias du fichier config/packages/sonata_media.yaml

sonata_media:
    class:
        media: App\Application\Sonata\MediaBundle\Entity\Media
        gallery: App\Application\Sonata\MediaBundle\Entity\Gallery
        gallery_item: App\Application\Sonata\MediaBundle\Entity\GalleryItem
        category: App\Application\Sonata\ClassificationBundle\Entity\SonataClassificationCategory

On ajoute notre bundle dans config/packages/doctrine.yaml

doctrine:
    dbal:
        url: '%env(resolve:DATABASE_URL)%'

        # IMPORTANT: You MUST configure your server version,
        # either here or in the DATABASE_URL env var (see .env file)
        #server_version: '13'
    orm:
        auto_generate_proxy_classes: true
        naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
        auto_mapping: true
        mappings:
            App:
                is_bundle: false
                dir: '%kernel.project_dir%/src/Entity'
                prefix: 'App\Entity'
                alias: App
            App\Application\Sonata\UserBundle:
                is_bundle: false
                dir: '%kernel.project_dir%/src/Application/Sonata/UserBundle/Entity'
                prefix: 'App\Application\Sonata\UserBundle\Entity'
                alias: App\Application\Sonata\UserBundle
            App\Application\Sonata\MediaBundle:
                is_bundle: false
                dir: '%kernel.project_dir%/src/Application/Sonata/MediaBundle/Entity'
                prefix: 'App\Application\Sonata\MediaBundle\Entity'
                alias: App\Application\Sonata\MediaBundle
            App\Application\Sonata\ClassificationBundle:
                is_bundle: false
                dir: '%kernel.project_dir%/src/Application/Sonata/ClassificationBundle/Entity'
                prefix: 'App\Application\Sonata\ClassificationBundle\Entity'
                alias: App\Application\Sonata\ClassificationBundle
                
            translatable:
                type: annotation # or attribute
                alias: Gedmo
                prefix: Gedmo\Translatable\Entity
                # make sure vendor library location is correct
                dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Translatable/Entity"
        filters:
            softdeleteable:
                class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter

On crée le schéma de la base de données.

 php8.1 bin/console doctrine:schema:update --force
Selection_280

On ajoute nos interfaces dans l’admin dans le fichier config/packages/sonata_admin.yaml

sonata_admin:
    title: Backoffice
    title_logo: /bundles/sonataadmin/images/logo_title.png
    show_mosaic_button: false
    security:
        handler: sonata.admin.security.handler.role
    options:
        default_admin_route: edit
        html5_validate: false
    global_search:
        admin_route: edit
    breadcrumbs:
        child_admin_route: edit
    dashboard:
        groups:
            runroom:
#                label: Base
#                items:
#                    - Runroom\SeoBundle\Admin\MetaInformationAdmin
#                    - Runroom\BasicPageBundle\Admin\BasicPageAdmin
#                    - Runroom\TranslationBundle\Admin\TranslationAdmin
#                    - Runroom\RedirectionBundle\Admin\RedirectAdmin
#                    - label: Cookies
#                      route: admin_runroom_cookies_cookiespage_edit
#                      route_params: { id: 1 }
            users:
                label: Users
                icon: <i class="fa fa-users"></i>
                on_top: true
                items:
                    - sonata.user.admin.user
            media:
                label: Media
                icon: <i class="fa fa-photo"></i>
                on_top: true
                items:
                    - sonata.media.admin.media
                    
            classification:
                label: Classification
                icon: <i class="fa fa-tags"></i>
                items:
                    - sonata.classification.admin.category
                    - sonata.classification.admin.tag
                    - sonata.classification.admin.collection
                    - sonata.classification.admin.context
                    

Et tout devrait fonctionner normalement.

Selection_285

On passe une commande de migration pour mettre à jour tous les éléments avec le contexte par défaut.
A priori on a pas beaucoup d’éléments à migrer à ce stade.

php8.1 bin/console sonata:classification:fix-context
Selection_282