Installation:
composer require sylius/taxonomy-bundle
Add to config/bundles.php:
return [
// ...
Sylius\Bundle\TaxonomyBundle\SyliusTaxonomyBundle::class => ['all' => true],
];
Database Migrations:
Run migrations to create taxon, taxon_parent, and taxon_string tables:
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
First Use Case: Create a taxonomy (e.g., "Product Categories") and taxons (e.g., "Electronics", "Clothing") via:
php bin/console sylius:taxonomy:create product_categories
php bin/console sylius:taxonomy:create-taxon product_categories Electronics
src/Entity/Taxon.php, src/Entity/TaxonTranslation.phpsrc/Command/ for CLI management.src/Resources/fixtures/ for sample data.Taxonomy Creation:
Define taxonomies (e.g., "Product Categories", "Blog Tags") and associate them with entities (e.g., Product, BlogPost).
# config/packages/sylius_taxonomy.yaml
sylius_taxonomy:
resources:
product_categories:
driver: doctrine/orm
classes:
taxon: App\Entity\ProductCategory
taxon_translation: App\Entity\ProductCategoryTranslation
Taxon Assignment:
Use the TaxonAwareInterface trait or embed taxons in entities:
use Sylius\Component\Taxonomy\Model\TaxonAwareInterface;
use Sylius\Component\Taxonomy\Model\TaxonAwareTrait;
class Product implements TaxonAwareInterface
{
use TaxonAwareTrait;
}
Assign taxons via repository or form:
$product->addTaxon($taxonRepository->findOneBy(['slug' => 'electronics']));
Hierarchical Management: Build nested taxonomies (e.g., "Electronics > Phones > iPhone"):
$parentTaxon = $taxonRepository->findOneBy(['slug' => 'electronics']);
$childTaxon = new Taxon();
$childTaxon->setParent($parentTaxon);
$childTaxon->setCode('phones');
$taxonManager->persist($childTaxon);
Translation Handling: Use translations for multilingual taxons:
$taxonTranslation = new TaxonTranslation();
$taxonTranslation->setLocale('en_US');
$taxonTranslation->setName('Phones');
$taxon->addTranslation($taxonTranslation);
# config/api/resources.yaml
resources:
Sylius\Bundle\TaxonomyBundle\Resources/config/api:
type: sylius.resource
collectionOperations:
get:
method: GET
path: /api/taxonomies/{id}/taxons
TaxonType for Symfony forms:
use Sylius\Bundle\TaxonomyBundle\Form\Type\TaxonAutocompleteType;
$builder->add('taxons', TaxonAutocompleteType::class, [
'taxonomy' => 'product_categories',
'multiple' => true,
]);
taxon.pre_persist or taxon.post_remove events for custom logic:
use Sylius\Component\Taxonomy\Event\TaxonEvent;
$eventDispatcher->addListener(TaxonEvent::PRE_PERSIST, function (TaxonEvent $event) {
$taxon = $event->getSubject();
$taxon->setSlug(Str::slug($taxon->getName()));
});
Circular References:
Avoid creating circular parent-child relationships (e.g., A → B → A). Validate in pre-persist listeners:
if ($taxon->getParent() && $taxon->getParent()->hasAncestor($taxon)) {
throw new \RuntimeException('Circular reference detected!');
}
Slug Uniqueness:
Slugs must be unique per taxonomy. Override TaxonSlugger to handle conflicts:
use Sylius\Component\Taxonomy\Generator\TaxonSluggerInterface;
class CustomTaxonSlugger implements TaxonSluggerInterface
{
public function generateSlug(TaxonInterface $taxon): string
{
$slug = Str::slug($taxon->getName());
$originalSlug = $slug;
$count = 1;
while ($taxon->getTaxonomy()->hasTaxonWithSlug($slug)) {
$slug = "{$originalSlug}-{$count}";
$count++;
}
return $slug;
}
}
Performance with Large Taxonomies:
Use DISTINCT queries or materialized paths for deep hierarchies. Example with materialized_path:
// Add to Taxon entity
#[ORM\Column(type: 'string', length: 255)]
private string $path = '/';
// Update in TaxonManager
$taxon->setPath($parent->getPath() . $taxon->getCode() . '/');
$taxon = $taxonRepository->findOneBy(['code' => 'electronics', 'taxonomy' => 'product_categories']);
taxon_translation table and is set in TaxonTranslation:
$taxonTranslation->setLocale('en_US'); // Must match entity's locale
Custom Taxon Fields:
Extend Taxon entity and update fixtures:
#[ORM\Entity]
class CustomTaxon extends Taxon
{
#[ORM\Column(type: 'integer', nullable: true)]
private ?int $priority = null;
}
Update TaxonRepository to handle custom queries.
Taxonomy Drivers:
Implement TaxonomyDriverInterface for non-Doctrine storage (e.g., Elasticsearch):
class ElasticsearchTaxonomyDriver implements TaxonomyDriverInterface
{
public function findTaxon(string $taxonomyCode, string $taxonCode): ?TaxonInterface
{
// Custom Elasticsearch logic
}
}
Validation:
Add constraints to Taxon or TaxonTranslation:
use Symfony\Component\Validator\Constraints as Assert;
#[Assert\Length(min: 2, max: 100)]
private ?string $name = null;
Symfony UX Autocomplete: Integrate with Symfony UX for live search:
{{ attribute_form_widget(attribute, {
'attr': {
'data-controller': 'sylius-taxon-autocomplete',
'data-sylius-taxon-autocomplete_taxonomy-value': 'product_categories'
}
}) }}
How can I help you explore Laravel packages today?