sylius/variable-product-bundle
Adds variable product support to Sylius, letting merchants create products with multiple options and variants and manage pricing, stock, and availability per variant. Integrates with the Sylius admin and storefront so configurable catalog items work out of the box.
First, verify you actually need it. This bundle is archived and read-only. Product variants and options live inside sylius/sylius core for modern versions (roughly 1.12+ and Sylius 2.x). You only pull this bundle in if you're maintaining a legacy project whose composer.json already requires it, or you're studying how variant generation used to be structured. Check your Sylius version constraint first:
composer show sylius/sylius | grep versions
composer why sylius/variable-product-bundle
If nothing depends on it, do not install it. Modern code should use Sylius\Component\Core\Model\ProductVariant and the sylius.generator.product_variant service in core.
Minimal setup (legacy Sylius):
composer require sylius/variable-product-bundle
Register it in AppKernel / bundles.php before SyliusCoreBundle, then update the schema:
bin/console doctrine:schema:update --force
# or bin/console doctrine:migrations:diff
Where to look first, in this order:
src/Model/ — ProductVariant, ProductOption, ProductOptionValue, their interfaces, and ProductVariantInterface relationships to ProductInterface.src/Resources/config/doctrine/*.orm.xml — the mapping that ties variants to products and options. This is where overrides break.src/Resources/config/services.xml — the services you can decorate (sylius.factory.product_variant, sylius.repository.product_variant, option repositories).src/Form/Type/ — ProductVariantType, ProductOptionType, ProductOptionValueType, and the variant selection form.First use case: create a product ("T-shirt") with options Size and Color, add option values (S/M/L, Red/Blue), then generate the cartesian product of variants and render a selector on the product page. In a template, the selection typically ends up as a form posting optionValues[] which resolves to a single ProductVariant via the repository.
Model extension via parameters (the standard Sylius pattern). Override the concrete classes in config.yml/services.yaml rather than editing the bundle:
parameters:
sylius.model.product_variant.class: App\Entity\ProductVariant
sylius.repository.product_variant.class: App\Repository\ProductVariantRepository
If you subclass ProductVariant, you must also repoint the Doctrine association's targetEntity or ensure ResolveTargetEntityListener maps Sylius\Component\VariableProductBundle\Model\ProductVariantInterface to your class. Forgetting this is the #1 source of "class not found" and wrong-class hydration errors.
Variant generation workflow. Use the generator service instead of hand-building combinations:
$generator = $this->get('sylius.generator.product_variant');
$generator->generate($product); // produces one ProductVariant per option-value combination
This is the pattern to reuse in fixtures, imports, and admin "generate variants" actions. Always persist after generation — the generator does not flush.
Fixtures for reproducible catalogs. Define options and variants in YAML and let sylius_fixtures handle it:
product:
options:
- name: Size
values: [S, M, L]
- name: Color
values: [Red, Blue]
variants:
- code: TSHIRT-S-RED
optionValues: [TSHIRT-S, TSHIRT-RED]
The optionValues key must reference option-value codes; silently empty variants are the usual symptom of a typo there.
Variant selector integration. Build a form with option-value choice fields, then resolve the selected combination to a variant through sylius.repository.product_variant using findOneByOptionValues() (or an equivalent custom query). Validate before rendering "add to cart" — a missing variant should produce a validation error, not a null object downstream.
Event hooks. Use sylius.product.pre_create / sylius.product.pre_update and the variant equivalents to enforce invariants (at least one variant, unique combinations) rather than overriding controllers.
Repository customization. Extend ProductVariantRepository for combination lookups and for eager-loading optionValues to avoid N+1 queries on listing pages.
sylius/sylius usually produces composer conflict hell or subtle runtime mismatches. Confirm the exact pair from the bundle's own composer.json before touching the lockfile.code, not on the option-value set. Two variants with identical option values are legal at the DB level and will confuse the selector. Enforce uniqueness in a pre_create/pre_update listener.ProductOptionValue leaves variants referencing stale references (or FK errors, depending on mapping). Add cleanup logic or restrict deletion.TranslatableListener conventions and always populate translations in fixtures.bin/console debug:container sylius.repository.product_variant
bin/console debug:container --parameter=sylius.model.product_variant.class
bin/console doctrine:schema:validate
bin/console doctrine:query:sql "SELECT id, code FROM sylius_product_variant"
doctrine:schema:validate and check doctrine:mapping:info for duplicate entity mappings. Sylius loads XML mapping from the bundle; your override must not leave both mapped.sylius.form.type.product_variant to add fields rather than copying the form. For the front-end selector, the form type is separate and typically lives in your app, not the bundle.Sylius\Component\Core\Model\*) and rehearse the data migration on a copy. There is no supported upgrade path from a read-only bundle; write your own and test on real data volume.How can I help you explore Laravel packages today?