Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Association Bundle Laravel Package

sylius/association-bundle

The Sylius Association Bundle adds product association management to Sylius stores, letting administrators group products into named association types such as related items, cross-sells, or accessories, with configurable types and admin UI for editing them.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: the package is archived and read-only. sylius/association-bundle is a monorepo split of Sylius/Sylius, published so the parent repo can be consumed as separate Composer packages. It is not developed, triaged, or patched independently — bug reports and PRs go to Sylius/Sylius. With ~0 dependents and 3 stars, treat it as an internal implementation detail of Sylius, not a library you adopt.

Practical entry point. Do not composer require sylius/association-bundle standalone for new work. Get it transitively via sylius/sylius (or sylius/product-bundle), which pins a compatible version. If you are building a non-Sylius Symfony app, depend on sylius/association (the component) directly — the bundle only adds DI wiring and Doctrine mappings, and you can replicate that in ~30 lines of services.yaml.

Where to look, in order:

  1. DependencyInjection/SyliusAssociationExtension — the whole bundle is essentially here. It registers the registry service and loads Doctrine XML mappings.
  2. Resources/config/doctrine/*.orm.xml — the mapping for AssociationType and Association. If you override models, this is what you must shadow.
  3. Sylius\Component\Association\Registry\AssociationTypeRegistry — the runtime API you actually interact with.
  4. AssociationTypesInterface — the built-in type name constants (RELATED, UP_SELL, CROSS_SELL).

First use case: add a new association type (e.g. "frequently bought together") between two models and expose it on the storefront. You register an AssociationType row keyed by a string name, then create Association entities that reference owner, associated object, and that type.

// services.yaml
services:
    App\Association\FrequentlyBoughtTogetherType:
        tags: ['sylius.association_type']

Enable the bundle in config/bundles.php (SyliusAssociationBundle is normally already enabled by the Sylius recipe). Then verify wiring with bin/console debug:container sylius.association_type.registry.

Implementation Patterns

Registry as the seam. Almost all interaction goes through Sylius\Component\Association\Registry\AssociationTypeRegistryInterface (sylius.association_type.registry). Inject it rather than the concrete class; it exposes getTypeByName(string $name): AssociationTypeInterface and getTypes(): iterable. Everything else in the bundle is Doctrine plumbing.

Custom association type via the registry. Two ways, and they differ in lifecycle:

// Runtime — registered on every container boot, no DB row until persisted.
final class ProductBundlesType implements AssociationTypeInterface
{
    public function getName(): string { return 'product_bundles'; }
    public function getLabel(): string { return 'sylius.association_type.product_bundles'; }
}

final class AssociationTypeRegistrar
{
    public function __construct(private AssociationTypeRegistryInterface $registry) {}

    public function register(): void
    {
        $this->registry->addType(new ProductBundlesType()); // name may differ by version
    }
}

The DB-backed route (a AssociationType entity row plus a Factory/fixture) is what Sylius itself uses, and it is the safer one because the admin UI and fixtures read from the repository. Prefer it unless you have a reason not to.

Adding associations to an aggregate. Associations are not automatically reciprocal. Creating an association from $a to $b writes one row with $a as owner and $b as associated. If your domain needs the reverse to be visible from $b, add the second Association explicitly — do not expect the model to synthesize the inverse.

$association = $associationFactory->createNew();
$association->setOwner($product);
$association->setAssociated($relatedProduct);
$association->setType($registry->getTypeByName(AssociationTypesInterface::RELATED));
$product->addAssociation($association); // check the actual collection API for your version

Querying by type. Filtering through the owning collection is fine for a handful of rows and terrible at scale; the collection is loaded eagerly by Doctrine's default. For listing pages, use the association repository with a JOIN on association_type.name and paginate there rather than hydrating a full Product aggregate.

Integration with forms. The bundle does not ship user-facing form types; those live in ProductBundle/AdminBundle. For a custom type, build the form yourself: an EntityType over your associated resource, plus a hidden/choice field for the association type name. Reuse Sylius' translation domain (sylius.association_type) for labels so admin columns render consistently.

Resource overrides, not subclassing the bundle. To add a field to AssociationType (an icon, a sort order, a description), override the model class in config (sylius.association_type.class) with a class extending the base model, and shadow the Doctrine XML in your app config. Never extend the bundle's PHP classes — they carry no BC promise and the split repo means they change without notice.

Fixtures. Register association types as reference data (idempotent, load-before-products in your fixture order). If you load them in the same pass as products, you will hit foreign-key ordering failures on fresh databases.

Gotchas and Tips

  • Archived means frozen. No security patches, no Symfony 7/8 compatibility work, no PHP version bumps. Your real version constraint is whatever sylius/sylius pins — pin the monorepo, let it pin this, and never float this package independently.
  • Read-only split. Issues and PRs are closed on this repo. If you find a bug, reproduce it against Sylius/Sylius and file there; otherwise you are patching a package that will be overwritten on the next composer update.
  • The name is the primary key. Association types are identified by the name string, not an integer ID. Renaming a type in a fixture or constant orphans every existing association row and silently breaks storefront listings. Treat names as immutable identifiers; put human copy in the label/translation, never in the name.
  • Registry population is per-request. A registry populated at runtime means the type list is whatever the DB holds at that instant. In tests this bites: kernel-rebooted functional tests can see a different type set than a long-lived process. Use a DB-backed registry plus fixtures to get determinism, or reset the registry between tests.
  • Doctrine mapping shadowing is all-or-nothing. If you override Association to add a field, copy the entire mapping XML into your bundle and remap the resource. A partial override leaves fields unmapped and Doctrine fails at schema-tool time with an unhelpful "no mapping" error.
  • Watch for duplicate rows. Nothing at the DB level enforces uniqueness of (owner, associated, type). Programmatic double-adds are easy and produce duplicate related-product blocks on the storefront. Add a unique constraint in your own migration if you own the schema.
  • N+1 on listing pages is the default failure mode. A product grid that renders an association column will issue one query per row. Fix it in the grid's query builder, not in the template.
  • Debugging order: debug:container to confirm the registry service exists → debug:doctrine on the two entities to confirm mapping → check the association_type table is actually populated → only then look at your service code. The failure is almost always missing reference data, not broken wiring.
  • Cleanest extension point is the registry interface and the resource config (sylius.association_type.class, sylius.association.class). Both survive minor upgrades. Subclassing bundle internals does not.
  • If you are not on Sylius, skip this package. Wire sylius/association into your own bundle: one service definition for the registry, one doctrine.orm.mappings entry pointing at the component's XML. That is the entire content of this repo, and you get a dependency you can actually patch.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console