sonata-project/classification-bundle
Symfony bundle providing a classification system for Sonata: categories, tags and collections management with admin integration, persistence support and documentation. Part of the Sonata Project ecosystem.
Installation
composer require sonata-project/classification-bundle
Ensure SonataClassificationBundle is enabled in config/bundles.php:
SonataClassificationBundle\SonataClassificationBundle::class => ['all' => true],
Database Migrations Run the bundle's migrations to create the required tables:
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
First Use Case: Admin Panel The bundle provides built-in admin interfaces for managing:
Enable the admin in config/packages/sonata_admin.yaml:
sonata_admin:
options:
html5_validation: true
security:
handler: sonata.admin.security.handler.acl
assets:
less_filters: ['less', 'cssrewrite']
templates:
layout: 'SonataClassificationBundle::standard_layout.html.twig'
Register the admin services in config/services.yaml:
SonataClassificationBundle\:
resource: '../vendor/sonata-project/classification-bundle/Resources/config/services.xml'
tags: ['controller.service_arguments']
Access the Admin Panel
Visit /admin/classification to manage classifications via SonataAdmin.
$categoryManager = $this->container->get('sonata.classification.category_manager');
$rootCategory = $categoryManager->createRootCategory('Root Name', 'slug-root');
$categoryManager->save($rootCategory);
$subCategory = $categoryManager->createChildCategory($rootCategory, 'Sub Name', 'slug-sub');
$categoryManager->save($subCategory);
$categories = $categoryManager->getRootCategories();
foreach ($categories as $category) {
$subCategories = $categoryManager->getSubCategories($category);
// ...
}
$tagManager = $this->container->get('sonata.classification.tag_manager');
$tag = $tagManager->findOneBy(['name' => 'Featured']);
if (!$tag) {
$tag = $tagManager->create('Featured');
$tagManager->save($tag);
}
$entity->setTags([$tag]); // Assuming your entity has a `tags` relation.
$contextManager = $this->container->get('sonata.classification.context_manager');
$context = $contextManager->create('Products', 'products');
$contextManager->save($context);
$categoryManager->setContext($rootCategory, $context);
$tagManager->setContext($tag, $context);
$collectionManager = $this->container->get('sonata.classification.collection_manager');
$collection = $collectionManager->create('Promotions', 'promotions');
$collectionManager->save($collection);
$collectionManager->addTagToCollection($tag, $collection);
Extend Admin Classes (if needed):
use Sonata\ClassificationBundle\Admin\CategoryAdmin as BaseCategoryAdmin;
class CustomCategoryAdmin extends BaseCategoryAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('customField', 'text')
->add('parent', 'sonata_type_model', [
'by_reference' => false,
]);
}
}
Register the custom admin in config/services.yaml:
services:
App\Admin\CustomCategoryAdmin:
arguments: ['@sonata.admin.orm_admin', '@doctrine.orm.entity_manager', '@sonata.classification.category_manager']
tags: ['sonata.admin', 'sonata.admin.category']
Use Classifications in Your Entities:
use Sonata\ClassificationBundle\Model\CategoryInterface;
use Sonata\ClassificationBundle\Model\TagInterface;
class Product
{
/**
* @ORM\ManyToMany(targetEntity="Sonata\ClassificationBundle\Model\Category")
*/
private $categories;
/**
* @ORM\ManyToMany(targetEntity="Sonata\ClassificationBundle\Model\Tag")
*/
private $tags;
}
Filtering in SonataAdmin:
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('categories', 'sonata_type_model_list', [
'model_manager' => $this->modelManager,
'property' => 'categories',
'btn_add' => false,
]);
}
$categoryManager = $this->container->get('sonata.classification.category_manager');
$categories = $categoryManager->getRootCategories(); // Returns a paginated list.
$tagManager = $this->container->get('sonata.classification.tag_manager');
$tags = $tagManager->findBy(['context' => $context]);
$contextManager = $this->container->get('sonata.classification.context_manager');
$contexts = $contextManager->findAll();
Lazy Loading in Categories:
$category->getChildren()->load(); // For Doctrine ORM.
disableChildrenLazyLoading() (deprecated in v3.18.0+).Context Assignment:
setContext() on the manager:
$categoryManager->setContext($category, $context);
Slug Generation:
Symfony 6+ Deprecations:
renderWithExtraParams (replaced by Twig’s native methods).Symfony\Component\HttpKernel\DependencyInjection\Extension alternatives if extending the bundle.Doctrine Cascade Merges:
doctrine/orm < 2.7, cascade merge may fail. Upgrade or handle manually:
$em->merge($category); // Explicit merge if cascade fails.
Missing Admin Buttons:
sonata_admin configuration. Ensure the admin is properly tagged:
tags: ['sonata.admin', 'sonata.admin.category']
Performance with Large Trees:
getSubCategoriesPager() for paginated results:
$pager = $categoryManager->getSubCategoriesPager($parentCategory, 1, 10);
Translation Issues:
{% trans from 'SonataClassificationBundle' %}
Resources/translations/messages.{locale}.yml.Schema Validation Errors:
php bin/console doctrine:schema:validate to check for mapping issues.xml validation is disabled if using newer Doctrine versions.Custom Entities:
Sonata\ClassificationBundle\Model\Category or Tag to add fields:
use Sonata\ClassificationBundle\Model\BaseCategory as BaseCategory;
class CustomCategory extends BaseCategory
{
/**
* @ORM\Column(type="string", nullable=true)
*/
private $customField;
}
CategoryAdmin to include the new field.Custom Managers:
CategoryManager) by extending and injecting dependencies:
class CustomCategoryManager extends CategoryManager
{
public function getCustomCategories()
{
// Custom logic.
}
}
services:
sonata.classification.category_manager:
How can I help you explore Laravel packages today?