#19241·Sylius

[AdminBundle][ShopBundle] "Options matching" can be set on a product without options, leaving only its first variant purchasable

Author: DarkorzerarkoCreated Sep 17, 2026Updated Sep 17, 2026
LabelsPotential Bug

Sylius version(s) affected

All 2.X

Description

Two rules in the admin product form do not take each other into account.

1. Options are locked once the product has a variant. src/Sylius/Bundle/ProductBundle/Form/EventSubscriber/ProductOptionFieldSubscriber.php (lines 48-53):

php
/** Options should be disabled for configurable product if it has at least one defined variant */
$disableOptions = null !== $this->variantResolver->getVariant($product);

$form->add('options', EntityType::class, [
    'required' => false,
    'disabled' => $disableOptions,
    // ...
]);

2. The variant selection method always offers both values. src/Sylius/Bundle/CoreBundle/Form/Extension/ProductTypeExtension.php (line 52) adds variantSelectionMethod with Variant choice and Options matching, and nothing validates that a product set to Options matching has any options to match on.

A configurable product that was created without options and then given variants can therefore no longer get options, but can still be switched to Options matching. The admin saves it with "Product has been successfully updated."

Image

What the shop does with such a product

In Options matching mode the product page renders the options hook instead of the variants table (src/Sylius/Bundle/ShopBundle/templates/product/show/content/info/summary/add_to_cart/variants.html.twig, lines 5-8). The add-to-cart variant field becomes a ProductVariantMatchType (src/Sylius/Bundle/CoreBundle/Form/Extension/CartItemTypeExtension.php, lines 54-63), which has one select per product option (ProductVariantMatchType.php, lines 36-40). With no options there are no selects, so the page shows only Quantity and Add to cart.

The displayed price comes from the cart item created by CartItemFactory::createForProduct(), which uses the variant resolver: the first enabled variant.

On submit, src/Sylius/Bundle/ProductBundle/Form/DataTransformer/ProductVariantToProductOptionsTransformer.php (lines 71-81) looks for a variant that has every selected option value:

php
private function matches(array $optionValues): ProductVariantInterface
{
    foreach ($this->product->getVariants() as $variant) {
        foreach ($optionValues as $optionValue) {
            if (null === $optionValue || !$variant->hasOptionValue($optionValue)) {
                continue 2;
            }
        }

        return $variant;
    }
    // ...
}

With no option values the inner loop never runs, so the first variant of getVariants() (ordered by position, then id) always matches, whether it is enabled or not. As a result:

  • Only the first variant can be bought. The other variants are not shown anywhere on the product page and cannot be added to the cart.
  • If the first variant is disabled, the page shows the price of the first enabled variant, but Add to cart still matches the disabled one and fails with "<product name> is not available." Nothing can be bought.

The same product in Variant choice mode works normally:

Variant choice Options matching
Image Image

How to reproduce

  1. Install Sylius Standard 2.2 with the default fixtures.
  2. In the admin panel go to Catalog → Products and choose Create → Configurable product. Fill in the code, name and slug, tick the Fashion Web Store channel, leave Options empty and save.
  3. Use Manage variants → Create variant to create two variants, e.g. "Red" priced $10.00 and "Blue" priced $20.00.
  4. Edit the product again. The Options field is now disabled. Change Variant selection method to Options matching and save.
  5. Open the product page in the shop and click Add to cart.

Expected: either the shop still lets the customer pick and buy any enabled variant, or the admin does not allow a variant selection method the product cannot support.

Actual: the product page shows only "$10.00", Quantity and Add to cart, with no way to choose a variant. Add to cart always adds "Red". "Blue" cannot be bought. After disabling "Red", the page shows "$20.00", but Add to cart fails with "Configurable product without options is not available." and the cart stays empty.

Possible Solution

Which fix is correct depends on whether Options matching is meant to be valid for a product without options.

A. Reject the combination in the admin. For example, a class constraint on Product that requires at least one option when the variant selection method is match, or a form that offers Options matching only when the product has options.

  • The shop never receives a product it cannot sell. A constraint would also cover the Admin API, which accepts variantSelectionMethod when a product is created (sylius:admin:product:create group in src/Sylius/Bundle/ApiBundle/Resources/config/serialization/Product.xml).
  • The locked Options field means an administrator who picked the wrong setup can only fix it by switching back to Variant choice, which the validation message would need to make clear.

B. Fall back to variant choice in the shop when the product has no options. CartItemTypeExtension and variants.html.twig would check product.hasOptions() in addition to the stored selection method.

  • Existing products saved in this state start working without data changes.
  • The admin keeps showing Options matching for a product that does not behave that way.

C. Both A and B.

Separately from the missing options, matches() iterates over all variants, including disabled ones, while the displayed price comes from the first enabled variant. That is what makes the product completely unpurchasable when its first variant is disabled.

Which behaviour does the team intend? I am happy to open a PR once that is decided.

Until then, switching the product back to Variant choice makes all variants purchasable again.

Possible Solution

  • Options locked when a variant exists: src/Sylius/Bundle/ProductBundle/Form/EventSubscriber/ProductOptionFieldSubscriber.php (lines 48-53)
  • Variant selection method field: src/Sylius/Bundle/CoreBundle/Form/Extension/ProductTypeExtension.php (line 52); rendered for non-simple products by src/Sylius/Bundle/AdminBundle/templates/product/form/sections/general/variant_selection_method.html.twig
  • No validation constraint references variantSelectionMethod (nothing under */Resources/config/validation)
  • Add-to-cart variant field: src/Sylius/Bundle/CoreBundle/Form/Extension/CartItemTypeExtension.php (lines 54-63)
  • Option selects: src/Sylius/Bundle/ProductBundle/Form/Type/ProductVariantMatchType.php (lines 36-40), rendered by src/Sylius/Bundle/ShopBundle/templates/product/show/content/info/summary/add_to_cart/options/list.html.twig
  • Variant matching: src/Sylius/Bundle/ProductBundle/Form/DataTransformer/ProductVariantToProductOptionsTransformer.php (matches(), lines 71-88)
  • Displayed variant: src/Sylius/Component/Core/Factory/CartItemFactory.php (createForProduct(), line 49), called from src/Sylius/Bundle/ShopBundle/Twig/Component/Product/AddToCartFormComponent.php (line 147)
  • Product::isSimple() is 1 === variants.count() && !hasOptions() (src/Sylius/Component/Product/Model/Product.php, line 323), so a product with two variants and no options is configurable

Additional Context

No response