We have seen in previous articles how to initialize a Symfony 6 + Sonata 5 project with media and users in a secure admin. We then looked at how to manage user localizations.
Now we will see how to install and manage classifications.
We proceed with the installation of the latest version to date.
php8.1 composer.phar require sonata-project/classification-bundle:5.x-dev
Apparently, we have a configuration error.
Unrecognized option "media" under "sonata_classification.class". Available
!! options are "category", "collection", "context", "tag".
We remove the media section from the sonata_classification.yaml configuration
sonata_classification:
class:
category: App\Entity\SonataClassificationCategory
collection: App\Entity\SonataClassificationCollection
context: App\Entity\SonataClassificationContext
tag: App\Entity\SonataClassificationTag
#media: App\Entity\SonataMediaMedia
We then encounter a new 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
It's not a big deal; we will configure everything so that it works on the first try.
We will first implement our entities in a directory other than src/app in order not to clutter our application with the overloads from our bundles. Just like UserBundle and MediaBundle, we put them in src/Application/Sonata/ClassificationBundle/Entity and we will create our 4 files.
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;
}
}
Next, we need to provide the paths of our classes in 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
We also need to add the category class path to the media definitions in the config/packages/sonata_media.yaml file
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
We add our bundle in 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
We create the database schema.
php8.1 bin/console doctrine:schema:update --force

We add our interfaces to the admin in the config/packages/sonata_admin.yaml file
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
And everything should work normally.

We run a migration command to update all elements with the default context.
Apparently, we don't have many elements to migrate at this stage.
php8.1 bin/console sonata:classification:fix-context
