tourze/catalog-bundle
Symfony bundle for catalog management in PHP applications. Provides Doctrine-backed entities, repositories, and services for organizing catalog items and categories, with integration points for admin panels, forms, and APIs so you can add structured catalog data without building it from scratch.
Start by pinning the version explicitly, since 2.0.0 is a major bump with no published migration notes:
composer require tourze/catalog-bundle:^2.0
Before writing any code, do three reads in this order:
composer.json of the package — check require for symfony/* vs illuminate/*. Tourze publishes a lot of Symfony-flavoured bundles, so confirm whether 2.0.0 ships a Laravel service provider (extra.laravel.providers) or expects manual wiring in config/app.php / bootstrap/providers.php.src/ layout — the directory names tell you the mental model: Entity/ or Model/, Repository/, Service/, EventSubscriber/, DependencyInjection/. That is your API surface. Anything under DependencyInjection/ or Resources/config/ is configuration you can override.https://github.com/tourze/catalog-bundle/compare/1.0.2...2.0.0. With an empty changelog body, the compare view is your only source of truth for BC breaks.Then publish and migrate:
php artisan vendor:publish --provider="Tourze\CatalogBundle\CatalogBundleServiceProvider" --list
php artisan vendor:publish --provider="Tourze\CatalogBundle\CatalogBundleServiceProvider" --tag=config
php artisan vendor:publish --provider="Tourze\CatalogBundle\CatalogBundleServiceProvider" --tag=migrations
php artisan migrate
First use case to validate the install end-to-end: create a two-level catalog (root → child), attach one item to the child, and read it back through the package's own repository/service rather than raw SQL. If that round-trips, your wiring is correct and you can start layering domain logic on top.
Resolve through the container, not new. Bundles of this type almost always bind a manager/service in the DI config. Inject the interface, not the concrete class — that keeps you compatible with the 2.x line where internals are most likely to churn:
public function __construct(
private readonly CatalogManagerInterface $catalog,
) {}
Treat the catalog as a tree, and load it as one. The dominant day-to-day cost in catalog packages is recursive queries. Two patterns that hold up:
whereIn.$ids = $this->catalog->descendantIds($rootId); // cheap, cacheable
$items = Item::whereIn('catalog_id', $ids)->paginate(24);
Keep a thin application layer over the package. Wrap the package's write operations in your own actions/services so that a future 2.x → 3.x rename touches one file:
final class AttachItemToCatalog
{
public function __construct(private readonly CatalogManagerInterface $catalog) {}
public function __invoke(Item $item, int $catalogId): void
{
$this->catalog->attach($item, $catalogId);
}
}
Use events as the integration seam. If the bundle dispatches domain events (check Event/ or EventSubscriber/), subscribe rather than editing vendor behaviour — reindexing search, busting caches, firing webhooks. This is the pattern least likely to break across majors.
Cache reads, invalidate on write. Catalog data is read-heavy and write-rare. Cache the tree and the id-resolution results under keys that include the catalog id, and flush them from the same actions that write. Do not cache the relationship queries themselves unless you also tag them.
Seed deterministic fixtures for tests. Rather than relying on package factories (which may not exist), build a small in-test helper that creates a root + child + item; that helper becomes your regression harness for the 2.0.0 upgrade.
Admin/API layers. For an API, expose the tree as a nested resource with a depth limit and a ?flat=1 escape hatch — deeply nested JSON is the usual source of frontend pain. For admin, prefer a single "tree" screen plus an item table filtered by selected node over per-node CRUD pages.
2.0.0 with an empty changelog is a red flag, not a formality. 1.0.2 → 2.0.0 is a semver-major, so assume breakage until proven otherwise. Read the compare diff specifically for:
Tourze\CatalogBundle\...),Do not trust vendor:publish blindly. Publishing migrations into your own database/migrations directory means you now own them; future package migrations will collide by class name or timestamp. Prefer loading migrations from the package (loadMigrationsFrom) if the provider supports it, and only publish the config.
Watch the framework mismatch. If the bundle is Symfony-first, its service definitions may rely on Symfony's DI (tags, autoconfigure, compiler passes) that Laravel's container ignores silently — you get "class not found" or un-injected constructor args, not a clear error. If you hit that, either wire the services manually in your own provider or look for a Laravel bridge package from the same vendor.
Tree queries will N+1 unless you stop them. Enable DB::listen or Laravel Debugbar on a category page early. A recursive children() accessor is the classic offender. Materialised path or nested-set columns, if present, need to be maintained through the package's own write methods — never ->update() the tree columns directly.
Ordering is usually a column, not a config. If the catalog renders unordered, look for sort/position/weight on the entity and make sure your write path sets it; most "sorting is broken" reports are actually "we never set the sort field".
Upgrade procedure that keeps you safe: create a branch, bump to ^2.0 with --with-dependencies, run composer why-not tourze/catalog-bundle 2.0.0 to see what's blocking, run your test suite, then diff the package's own config file against yours (git diff of the published config is the fastest way to spot renamed keys).
Debugging aids: php artisan about to confirm the provider is registered, php artisan vendor:publish --list to see available tags, and app()->make(SomePackageClass::class) in tinker to confirm the container can resolve the service before you wire it into a controller.
Extension points to prefer: published config, event subscribers, and decorated interfaces. Avoid extending concrete package classes — those are exactly what majors rewrite.
Pin the version in composer.json ("tourze/catalog-bundle": "2.0.*") until you have read the diff and characterised the 1.0.2 → 2.0.0 changes; an unpinned ^2.0 on an actively-released vendor package can drop a 2.1.0 on you mid-sprint.
How can I help you explore Laravel packages today?