Hemos visto en artículos anteriores cómo inicializar un proyecto Symfony 6 + Sonata 5 con multimedia y usuarios en un admin seguro. Luego, analizamos cómo gestionar las localizaciones de usuarios.
Ahora veremos cómo instalar y gestionar clasificaciones.
Procedemos con la instalación de la última versión hasta la fecha.
php8.1 composer.phar require sonata-project/classification-bundle:5.x-dev
Al parecer, tenemos un error de configuración.
Unrecognized option "media" under "sonata_classification.class". Available
!! options are "category", "collection", "context", "tag".
Eliminamos la sección de medios del archivo de configuración 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
Luego nos encontramos con un nuevo error.
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
No es un gran problema; configuraremos todo para que funcione a la primera.
Primero implementaremos nuestras entidades en un directorio distinto a src/app para no llenar nuestra aplicación con las sobrecargas de nuestros bundles. Al igual que con UserBundle y MediaBundle, los colocamos en src/Application/Sonata/ClassificationBundle/Entity y crearemos nuestros 4 archivos.
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;
}
}
Luego, necesitamos proporcionar las rutas de nuestras clases en 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
También necesitamos agregar la ruta de la clase de categoría a las definiciones de multimedia en el archivo 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
Agregamos nuestro bundle en 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
Creamos el esquema de la base de datos.
php8.1 bin/console doctrine:schema:update --force

Agregamos nuestras interfaces al admin en el archivo 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
Y todo debería funcionar normalmente.

Ejecutamos un comando de migración para actualizar todos los elementos con el contexto predeterminado.
Al parecer, no tenemos muchos elementos para migrar en esta etapa.
php8.1 bin/console sonata:classification:fix-context
