Product Variants

This section describes how the module maps product variants to Shopware and what configuration options are available.

Please refer to the documentation of mapping products for details since most of the concepts are basically the same for variants.

Mapping Table

The mapping of a variant is almost the same as the mapping of a normal product. There some differences though:

  • parent_id is a required field
  • options is a required field
  • Several fields that are required for parent products are inherited and therefore not required
Target Field Source Path
* parent_id parentProduct
referenced entity searched by tax.custom_fields.{{SICF}}
* options taxGroup.identifier
referenced entity searched by tax.custom_fields.{{SICF}}
tax_id taxGroup.identifier
referenced entity searched by tax.custom_fields.{{SICF}}
manufacturer_id manufacturer.identifier
referenced entity searched by product_manufacturer.custom_fields.{{SICF}}
unit_id basePriceCalculation.baseUnit.abbreviation
referenced entity searched by unit.translations.short_code or unit.custom_fields.{{SICF}}
active active
price defaultPrice.netPrice or productPriceLists
manufacturer_number productIdentifiers.mpn
ean productIdentifiers.ean
* product_number identifier
* stock availability.warehouses
is_closeout availability.clearanceSale
purchase_steps availability.purchaseSteps
max_purchase availability.maxOrderQuantity
min_purchase availability.minOrderQuantity
purchase_unit basePriceCalculation.sellingQuantity
* reference_unit basePriceCalculation.baseQuantity
weight packageDimensions.weight.value
width packageDimensions.width.value
height packageDimensions.height.value
length packageDimensions.length.value
* release_date generalInformation.publicationDate
T name generalInformation.name
T description generalInformation.description
T keywords generalInformation.metaInformation.metaKeywords
T meta_description generalInformation.metaInformation.metaDescription
T meta_title generalInformation.metaInformation.metaTitle
T pack_unit packageDimensions.packageUnit.name
T pack_unit_plural packageDimensions.packageUnit.namePlural
categories categoryRelations
tags tags
properties propertyOptionCollection
visibilities channels

Subsections

The following subsections are involved in mapping variants:

output
└───product
    └───product-validate-variants
    └───product-upsert-variants
  • There are only two dedicated subsections for variants since variants are cached and deleted in the product subsections
  • Parent products of variants must be available in Shopware upon their creation. So the validation and upsert of variants takes place after are the parent variants are finished.

Configuration

There is no dedicated configuration available for variants. Instead, you can activate the mapping of variants by setting the field mapVariants to true. This field is part of the product subsection.

{
    "subsections": {
        "product": {
            "...": "...",
            "mapVariants": false
        }
    }
}

Price Surcharges / Discounts for Variants

In the backend of Shopware it is possible to add surcharges or discounts to specific properties while generating variants. This behavior is not 1:1 transferable to the module and must be imitated by setting manual prices of the variants.

$yellowVariant->setPrice(new SellingPrice(250));

You can also set currency aware prices for variants.

Variant Validation

A product with an empty variant option collection is invalid.

Only one PropertyValue is allowed for every PropertyOption that is part of the variant option collection of a product. In other words: Assigning two PropertyValues to a PropertyOption that is part of the variant option collection will be handled as an invalid variant.

A second variant with the same combination of variant options is invalid.

Example / Guide

This example will explain how variants are mapped to Shopware and how you have to prepare your documents in order to map variants successfully. For this we are going to generate a shirt in different color and size variations. Please note that this guide is still work in progress.

Generating Variants in Shopware

First, let's take a look on how variants are generated in Shopware. This will not be discussed in detail here, but essentially the steps are the following:

  1. Select the product (main product / parent) you want to generate variants for
  2. Switch to the variants tab
  3. Start the variant generator
  4. Select the properties you want to generate variants from

More information can be found in the documentation of Shopware.

In step 4 we select the colors red and yellow as well as the clothing sizes small (S), medium (M) and large (L):

Product Variant Option Selection 1 Product Variant Option Selection 2

The confirmation window tells us that six variants will be generated: Product Variant Generation Conformation

Now we can find six variants in the administration panel: Product Variants

Generating Variant Documents

So how do we perform these steps with the output-module and synQup in general? It is actually pretty straight forward:

  1. Create the parent product
  2. Create the variant product(s) by assigning a variant option as well as the parent product

So in order to generate the six variants generated by Shopware we have to generate six variant documents as well.

Note that the following code examples are kept very minimalistic. Not every necessary step (like assigning taxes) is displayed since this is not the topic of this guide.

# parent
$shirt = $this->generateProduct(identifier: '_ex_docs_shirt', name: 'Shirt');
$shirt->setActive(true);
$shirt->setHasVariants(true);
$this->persistAndFlush($shirt);

# variants

## red small
$red_small = $this->generateProduct(identifier: '_ex_docs_shirt_red_small', name: 'red - small');
$red_small->setParentProduct($shirt);
$red_small_options = [
    ColorOptions::getPropertyOption([ColorOptions::red()]),
    ClothingSizeOptions::getPropertyOption([ClothingSizeOptions::small()])
];
$red_small->setVariantOptionCollection(new PropertyOptionCollection($red_small_options));

## red large
$red_large = $this->generateProduct(identifier: '_ex_docs_shirt_red_large', name: 'red - large');
$red_large->setParentProduct($shirt);
$red_large_options = [
    ColorOptions::getPropertyOption(values: [ColorOptions::red()]),
    ClothingSizeOptions::getPropertyOption(values: [ClothingSizeOptions::large()])
];
$red_large->setVariantOptionCollection(new PropertyOptionCollection($red_large_options));

## yellow small
$yellow_small = $this->generateProduct(identifier: '_ex_docs_shirt_yellow_small', name: 'yellow - small');
$yellow_small->setParentProduct($shirt);
$yellow_small_options = [
    ColorOptions::getPropertyOption(values: [ColorOptions::yellow()]),
    ClothingSizeOptions::getPropertyOption(values: [ClothingSizeOptions::small()])
];
$yellow_small->setVariantOptionCollection(new PropertyOptionCollection($yellow_small_options));

## yellow medium
$yellow_medium = $this->generateProduct(identifier: '_ex_docs_shirt_yellow_medium', name: 'yellow - medium');
$yellow_medium->setParentProduct($shirt);
$yellow_medium_options = [
    ColorOptions::getPropertyOption(values: [ColorOptions::yellow()]),
    ClothingSizeOptions::getPropertyOption(values: [ClothingSizeOptions::medium()])
];
$yellow_medium->setVariantOptionCollection(new PropertyOptionCollection($yellow_medium_options));

## yellow large
$yellow_large = $this->generateProduct(identifier: '_ex_docs_shirt_yellow_large', name: 'yellow - large');
$yellow_large->setParentProduct($shirt);
$yellow_large_options = [
    ColorOptions::getPropertyOption(values: [ColorOptions::yellow()]),
    ClothingSizeOptions::getPropertyOption(values: [ClothingSizeOptions::large()])
];
$yellow_large->setVariantOptionCollection(new PropertyOptionCollection($yellow_large_options));

$this->persistAndFlush($red_small, $red_large, $yellow_small, $yellow_medium, $yellow_large);

After the next execution of the module the parent product and its variants will be available in the target shop.

Used Helpers

To make the examples more understandable the used helpers are listed below.

use Elio\CommerceBundle\Document\Product\Properties\PropertyOption;
use Elio\CommerceBundle\Document\Product\Properties\PropertyOptionCollection;
use Elio\CommerceBundle\Document\Product\Properties\PropertyValue;
use Elio\CommerceBundle\Document\Product\Properties\PropertyValueCollection;
use Elio\CommonBundle\Definition\Locale;
use Elio\CommonBundle\Document\Translation\TranslationCollection;

class ColorOptions
{
    public static function getPropertyOptionCollection(array $propertyValues): PropertyOptionCollection
    {
        return new PropertyOptionCollection([self::getPropertyOption($propertyValues)]);
    }

    public static function getPropertyOption(array $values = []): PropertyOption
    {
        return new PropertyOption(
            'color',
            'Color',
            TranslationCollection::create([Locale::en_GB, 'Color'], [Locale::de_DE, 'Farbe']),
            new PropertyValueCollection($values)
        );
    }

    public static function red(): PropertyValue
    {
        return new PropertyValue(
            'color_red',
            "red",
            TranslationCollection::create([Locale::en_GB, 'Red'], [Locale::de_DE, 'Rot'])
        );
    }

    public static function yellow(): PropertyValue
    {
        return new PropertyValue(
            'color_yellow',
            "yellow",
            TranslationCollection::create([Locale::en_GB, 'Yellow'], [Locale::de_DE, 'Gelb'])
        );
    }

    public static function blue(): PropertyValue
    {
        return new PropertyValue(
            'color_blue',
            "blue",
            TranslationCollection::create([Locale::en_GB, 'Blue'], [Locale::de_DE, 'Blau'])
        );
    }

    public static function white(): PropertyValue
    {
        return new PropertyValue(
            'color_white',
            "white",
            TranslationCollection::create([Locale::en_GB, 'White'], [Locale::de_DE, 'Weiß'])
        );
    }
}



use Elio\CommerceBundle\Document\Product\Properties\PropertyOption;
use Elio\CommerceBundle\Document\Product\Properties\PropertyOptionCollection;
use Elio\CommerceBundle\Document\Product\Properties\PropertyValue;
use Elio\CommerceBundle\Document\Product\Properties\PropertyValueCollection;
use Elio\CommonBundle\Definition\Locale;
use Elio\CommonBundle\Document\Translation\TranslationCollection;

class ClothingSizeOptions
{
    public static function getPropertyOptionCollection(array $propertyValues): PropertyOptionCollection
    {
        return new PropertyOptionCollection([self::getPropertyOption($propertyValues)]);
    }

    public static function getPropertyOption(array $values = []): PropertyOption
    {
        return new PropertyOption(
            'Clothing Size',
            'Clothing Size',
            TranslationCollection::create([Locale::en_GB, 'Clothing Size'], [Locale::de_DE, 'Kleidergröße']),
            new PropertyValueCollection($values)
        );
    }

    public static function small(): PropertyValue
    {
        return new PropertyValue(
            'clothing_size_small',
            "small",
            TranslationCollection::create([Locale::en_GB, 'S'], [Locale::de_DE, 'S'])
        );
    }

    public static function medium(): PropertyValue
    {
        return new PropertyValue(
            'color_medium',
            "medium",
            TranslationCollection::create([Locale::en_GB, 'M'], [Locale::de_DE, 'M'])
        );
    }

    public static function large(): PropertyValue
    {
        return new PropertyValue(
            'color_large',
            "large",
            TranslationCollection::create([Locale::en_GB, 'L'], [Locale::de_DE, 'L'])
        );
    }

}