sylius/taxonomies-bundle
Sylius Taxonomies Bundle adds taxonomy management to Sylius e-commerce apps, letting you organize products into hierarchical, translatable taxons such as categories and tags via the admin panel.
Before you type composer require, open the repo's composer.json and read the require block. This is a Symfony2-era bundle (the pjedrzejewski/SyliusTaxonomiesBundle repository predates the rename to sylius/taxonomy-bundle), and its framework/PHP constraints almost certainly cap you at Symfony 2.8 / PHP 5.6–7.x. With zero stars and zero dependents, treat it as legacy-only: viable for maintaining an existing app, not for new projects.
Minimal setup on a Symfony 2.x app:
// app/AppKernel.php
$bundles = [
// ...
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new Sylius\Bundle\TaxonomiesBundle\SyliusTaxonomiesBundle(),
];
# app/config/config.yml
stof_doctrine_extensions:
orm:
default:
tree: true # the Taxon entity is a nested-set node
The Doctrine tree listener is non-negotiable — the bundle maps Taxon as a Gedmo nested-set tree, and if the listener isn't registered you get a flat table that silently breaks every tree method.
Where to look first, in this order:
Model/TaxonomyInterface.php and Model/TaxonInterface.php — this is the contract; everything else is wiring.Entity/Taxonomy.php / Entity/Taxon.php — concrete Doctrine mappings (usually XML in Resources/config/doctrine/).DependencyInjection/Configuration.php — shows the classes and driver knobs you'll use for extension.Repository/TaxonRepository.php — the query helpers you'll actually call from controllers.First use case — a nested "Categories" taxonomy:
$taxonomy = new Taxonomy();
$taxonomy->setName('Categories');
$root = new Taxon();
$root->setName('Clothing');
$root->setTaxonomy($taxonomy);
$child = new Taxon();
$child->setName('T-Shirts');
$child->setParent($root);
$taxonomy->addTaxon($root);
$em->persist($taxonomy);
$em->flush();
echo $child->getPath(); // /clothing/t-shirts
One taxonomy per concern. The bundle is a generic container, so use separate Taxonomy rows for Category, Brand, and Tag rather than overloading a single tree. For tags/facets, keep the taxon tree one level deep — nested sets are wasteful for flat vocabularies and the tree maintenance cost (see Gotchas) is real.
Extend via the resource/DI config, not by editing vendor. This is the Sylius pattern and the bundle's main extension point:
# app/config/config.yml
sylius_taxonomies:
driver: doctrine/orm
classes:
taxonomy:
model: AppBundle\Entity\Taxonomy
taxon:
model: AppBundle\Entity\Taxon
namespace AppBundle\Entity;
use Sylius\Bundle\TaxonomiesBundle\Model\Taxon as BaseTaxon;
class Taxon extends BaseTaxon
{
// add your own relations, e.g. a Product many-to-many
}
Map your subclass with your own Doctrine metadata (XML/YAML) in AppBundle; do not annotate the parent. Forget the classes block and the DI will hand you base classes while Doctrine persists your subclass — a confusing half-broken state.
Attaching domain entities. Wire products/posts to Taxon with a many-to-many join table. Resolving "everything in this category and below" requires the subtree, not just the node:
$taxons = $taxonRepository->getChildren($taxon, false, null, 'ASC', true);
$taxons[] = $taxon;
$products = $productRepository->createQueryBuilder('p')
->innerJoin('p.taxons', 't')
->where('t IN (:taxons)')
->setParameter('taxons', $taxons)
->getQuery()
->getResult();
Watch N+1 here — add ->addSelect('t') or a fetch join on p.taxons if you render taxon names per product.
Rendering trees. Two approaches: a recursive Twig macro over getChildren(), or one childrenHierarchy() call from Gedmo's tree repository followed by a single-pass render of the nested array. The second avoids per-node queries and is the one to use for menu generation.
Breadcrumbs come free from the materialized path:
$trail = [];
$node = $taxon;
while ($node = $node->getParent()) {
$trail[] = $node;
}
// reverse for root-to-leaf order
Admin/routing. If the bundle version you have ships route imports (Resources/config/routing/…), prefix them under /{_locale}/admin in app/config/routing.yml. Older releases assume the Sylius admin stack; on a plain Symfony app you'll likely reimplement the CRUD with forms in an afternoon rather than fight the routing.
pjedrzejewski/SyliusTaxonomiesBundle, but the installable package is sylius/taxonomies-bundle (and later sylius/taxonomy-bundle). Confirm which one you actually pulled; forks diverge in mapping and interface names.getPath() and the permalink are stored, not computed. Renaming a taxon or re-parenting a subtree requires an explicit update/rebuild; the bundle doesn't guarantee uniqueness on slug or permalink, so enforce a unique constraint in your own schema.onDelete config. Decide explicitly whether delete means "delete subtree" or "re-parent children," implement it in your service, and test both.verify() and recover() are your escape hatch. Keep them handy.getChildren() returns empty, getLevel() is null, getPath() is blank. Check stof_doctrine_extensions.orm.default.tree: true and that the bundle is registered before Doctrine's mappings load.gedmo/doctrine-extensions (or a simple parent_id + recursive CTE on Postgres/MySQL 8) gives you the same capability with a maintained dependency. The bundle's remaining value is as a reference implementation of the Sylius resource/DI extension pattern — read DependencyInjection/Configuration.php even if you don't install it.How can I help you explore Laravel packages today?