Product Variant

A product variant represents a specific combination of selectable option values. After a customer or visitor of the shop selected on of more linked option values of a specific product, then a product variant can be determined. This way the customers can specify the product they want to buy, like a red or blue t-shirt or a specific pattern. If a product has one or more variants only its variants can be added to the shopping cart.

The product variants can be combined with product options.

Functionally this domain/system can be compared with the old product properties. The main difference is here that it's no longer based on the properties.

Product variant domain

The product variant domain provides management functionality (create, read, update and delete), as well as the possibility to filter all existing variants. This domain references a combination based on option and option values of the option domain.

Aggregate root and domain model

The aggregate root Gambio\Admin\Modules\ProductVariant\Model\ProductVariant is one specific combination of selectable option values linked to a specific product. A product variant encapsulates general information like a model number, EAN, stock, price, etc. that overwrite the corresponding attributes of the referenced product.

After creation, the aggregate root provides the possibility to change the attributes of the product variant itself.

Use cases using read service

Fetching all or a specific variant

/** $readService \Gambio\Admin\Modules\ProductVariant\Services\ProductVariantReadService **/

$allProductVariants     = $readService->getProductVariantsByProductId($productId = 1);
$specificProductVariant = $readService->getProductVariantById($variantId = 1);

Use cases using write service

Creating a new product variant

use Gambio\Admin\Modules\ProductVariant\Model\ValueObjects\ProductCustomization;
use Gambio\Admin\Modules\ProductVariant\Model\ValueObjects\ProductVariantStock;

/** $writeService \Gambio\Admin\Modules\ProductVariant\Services\ProductVariantWriteService **/
/** $factory \Gambio\Admin\Modules\ProductVariant\Services\ProductVariantFactory **/

$combination                  = $factory->createOptionAndOptionValueIds(
    $factory->createOptionAndOptionValueId($optionId1 = 1, $optionValueId1 = 1),
    $factory->createOptionAndOptionValueId($optionId2 = 2, $optionValueId2 = 1)
);
$imageListId                  = 1; // Can also be null if no image list exists for this product variant
$productCustomization         = $factory->createProductCustomization(
    $deliveryTimeId = 1, $priceType = ProductCustomization::PRICE_TYPE_ADDITION, $price = 0,
    $weightType = ProductCustomization::WEIGHT_TYPE_ADDITION, $weight = 0, $vpeScalarValue = 0, $vpeUnitId = null
);
$productIdentificationNumbers = $factory->createProductIdentificationNumbers(
    $modelNumber = 'abc123', $ean = '978020137962', $gtin = '04012345123456', $asin = 'B088W5PS35'
);
$stock                        = $factory->createProductVariantStock(
    $stock = 0, $stockType = ProductVariantStock::STOCK_TYPE_NOT_MANAGED
);

$id = $writeService->createProductVariant(
    $productId = 1, $combination, $imageListId, $productCustomization,
    $productIdentificationNumbers, $stock, $sortOrder = 1
);

Creating multiple product variants at once

use Gambio\Admin\Modules\ProductVariant\Model\ValueObjects\ProductCustomization;
use Gambio\Admin\Modules\ProductVariant\Model\ValueObjects\ProductVariantStock;

/** $writeService \Gambio\Admin\Modules\ProductVariant\Services\ProductVariantWriteService **/
/** $factory \Gambio\Admin\Modules\ProductVariant\Services\ProductVariantFactory **/

$productIds                   = [
    'first-product-variant'  => 1, 
    'second-product-variant' => 1,
];
$combinations                 = [
    'first-product-variant'  => $factory->createOptionAndOptionValueIds(
        $factory->createOptionAndOptionValueId($optionId1 = 1, $optionValueId1 = 1),
        $factory->createOptionAndOptionValueId($optionId2 = 2, $optionValueId2 = 1)
    ),
    'second-product-variant' => $factory->createOptionAndOptionValueIds(
        $factory->createOptionAndOptionValueId($optionId1 = 1, $optionValueId1 = 1),
        $factory->createOptionAndOptionValueId($optionId2 = 2, $optionValueId2 = 2)
    ),
];
$imageListIds                 = [
    'first-product-variant'  => 1, 
    'second-product-variant' => null,
];
$productCustomizations        = [
    'first-product-variant'  => $factory->createProductCustomization(
        $deliveryTimeId = 1, $priceType = ProductCustomization::PRICE_TYPE_ADDITION, $price = 0,
        $weightType = ProductCustomization::WEIGHT_TYPE_ADDITION, $weight = 0, $vpeScalarValue = 0, $vpeUnitId = null
    ),
    'second-product-variant' => $factory->createProductCustomization(
        $deliveryTimeId = 1, $priceType = ProductCustomization::PRICE_TYPE_REPLACING, $price = 56.78,
        $weightType = ProductCustomization::WEIGHT_TYPE_REPLACING, $weight = 12.34, $vpeScalarValue = 0, $vpeUnitId = null
    ),
];
$productIdentificationNumbers = [
    'first-product-variant'  => $factory->createProductIdentificationNumbers(
        $modelNumber = 'abc123', $ean = '978020137962', $gtin = '04012345123456', $asin = 'B088W5PS35'
    ),
    'second-product-variant' => $factory->createProductIdentificationNumbers(
        $modelNumber = 'abc123', $ean = '978020137962', $gtin = '04012345123456', $asin = 'B088W5PS35'
    ),
];
$stocks                       = [
    'first-product-variant'  => $factory->createProductVariantStock(
        $stock = 0, $stockType = ProductVariantStock::STOCK_TYPE_NOT_MANAGED
    ),
    'second-product-variant' => $factory->createProductVariantStock(
        $stock = 0, $stockType = ProductVariantStock::STOCK_TYPE_NOT_MANAGED
    ),
];
$sortOrders                   = [
    'first-product-variant'  => 1, 
    'second-product-variant' => 2,
];;

$creationArguments = [
    [
        $productIds['first-product-variant'], $combinations['first-product-variant'],
        $imageListIds['first-product-variant'], $productCustomizations['first-product-variant'],
        $productIdentificationNumbers['first-product-variant'], $stocks['first-product-variant'],
        $sortOrders['first-product-variant']
    ],
    [
        $productIds['second-product-variant'], $combinations['second-product-variant'],
        $imageListIds['second-product-variant'], $productCustomizations['second-product-variant'],
        $productIdentificationNumbers['second-product-variant'], $stocks['second-product-variant'],
        $sortOrders['second-product-variant']
    ],
];

$ids = $writeService->createMultipleProductVariants(...$creationArguments);

Updating the product variants combination

/** $readService \Gambio\Admin\Modules\Variant\Services\VariantReadService **/
/** $writeService \Gambio\Admin\Modules\Variant\Services\VariantWriteService **/
/** $factory \Gambio\Admin\Modules\Variant\Services\VariantFactory **/

$newCombination = $factory->createOptionAndOptionValueIds(
    $factory->createOptionAndOptionValueId($optionId1 = 1, $optionValueId1 = 1),
    $factory->createOptionAndOptionValueId($optionId2 = 2, $optionValueId2 = 1)
);

$productVariant = $readService->getProductVariantById($productVariantId = 1);
$productVariant->changeCombination($newCombination);

$writeService->storeProductVariants($productVariant);

Updating the product variants stock

use Gambio\Admin\Modules\ProductVariant\Model\ValueObjects\ProductVariantStock;

/** $readService \Gambio\Admin\Modules\Variant\Services\VariantReadService **/
/** $writeService \Gambio\Admin\Modules\Variant\Services\VariantWriteService **/
/** $factory \Gambio\Admin\Modules\Variant\Services\VariantFactory **/

$newStock = $factory->createProductVariantStock($stock = 0, $stockType = ProductVariantStock::STOCK_TYPE_NOT_MANAGED);

$productVariant = $readService->getProductVariantById($productVariantId = 1);
$productVariant->changeStock($newStock);

$writeService->storeProductVariants($productVariant);

Updating the product variants image list ID

/** $readService \Gambio\Admin\Modules\Variant\Services\VariantReadService **/
/** $writeService \Gambio\Admin\Modules\Variant\Services\VariantWriteService **/
/** $factory \Gambio\Admin\Modules\Variant\Services\VariantFactory **/

$productVariant = $readService->getProductVariantById($productVariantId = 1);
$productVariant->changeImageListId($factory->createImageListId($imageListId = 1));

$writeService->storeProductVariants($productVariant);

Updating the product variants sort order

/** $readService \Gambio\Admin\Modules\Variant\Services\VariantReadService **/
/** $writeService \Gambio\Admin\Modules\Variant\Services\VariantWriteService **/

$productVariant = $readService->getProductVariantById($productVariantId = 1);
$productVariant->changeSortOrder($newSortOrder = 2);

$writeService->storeProductVariants($productVariant);

Updating the product variants sort order

use Gambio\Admin\Modules\ProductVariant\Model\ValueObjects\ProductCustomization;

/** $readService \Gambio\Admin\Modules\Variant\Services\VariantReadService **/
/** $writeService \Gambio\Admin\Modules\Variant\Services\VariantWriteService **/

$newProductCustomization = $factory->createProductCustomization(
    $deliveryTimeId = 1, $priceType = ProductCustomization::PRICE_TYPE_ADDITION, $price = 0,
    $weightType = ProductCustomization::WEIGHT_TYPE_ADDITION, $weight = 0, $vpeScalarValue = 0, $vpeUnitId = null
);

$productVariant = $readService->getProductVariantById($productVariantId = 1);
$productVariant->changeProductCustomization($newProductCustomization);

$writeService->storeProductVariants($productVariant);

Updating the product variants sort order

/** $readService \Gambio\Admin\Modules\Variant\Services\VariantReadService **/
/** $writeService \Gambio\Admin\Modules\Variant\Services\VariantWriteService **/

$newProductIdentificationNumbers = $factory->createProductIdentificationNumbers(
    $modelNumber = 'abc123', $ean = '978020137962', $gtin = '04012345123456', $asin = 'B088W5PS35'
);

$productVariant = $readService->getProductVariantById($productVariantId = 1);
$productVariant->changeProductIdentificationNumbers($newProductIdentificationNumbers);

$writeService->storeProductVariants($productVariant);

Deleting a product variant

/** $writeService \Gambio\Admin\Modules\Variant\Services\VariantWriteService **/

$productVariantIds = [1, 2];

$writeService->deleteVariants(...$productVariantIds);

Use cases using filter service

Filter all existing variants including sorting and pagination

/** $filterService \Gambio\Admin\Modules\ProductVariant\Services\ProductVariantFilterService **/

$productId = 1;
$filters = [
    'combination.optionId' => 1, // Product variants based on the option with the ID 1
];
$sorting = '-stock'; // In descending order of the stock

$filteredProductVariants             = $filterService->filterProductVariants(
    $productId, $filters, $sorting, $limit = 25, $offset = 0
);
$totalCountOfFilteredProductVariants = $filterService->getProductVariantsTotalCount($productId, $filters);
Filtering

The filter array that is given to the filter service maps the attributes of the product variant and the filtering term. The assigned string (e.g. get|2020-01-01) always contains the comparison value, but it also may contain an operation (e.g. gte for greater than or equals to). Leaving the operation (e.g. 2020-01-01) will be the same as using equals to (eq).

The following table shows all attributes and the operations that can be used on them.

like (*) equals to (eq) lower than (lt) lower than or equals to (lte) greater than (gt) greater than or equals to (gte)
id X X X X X
productId X X X X X
combination.optionId X X X X X
combination.optionValueId X X X X X
sortOrder X X X X X
modelNumber X X
GTIN X X
ASIN X X
EAN X X
stockType X X
stock X X X X X
weightType X X
weight X X X X X
priceType X X
price X X X X X
vpeScalarValue X X X X X
vpeUnitId X X X X X
deliveryTimeId X X X X X
imageListId X X X X X
Sorting

To change the sorting, you can provide a string that describes the sorting order. The string must contain the attributes used for sorting. If there is a minus (-) in front of the attribute, it will be sorted in descending order. You can use multiple attributes to change the sorting order by linking them with a comma (,).

Business rules

  • The combination of option values must be uniq for a specific product. (Important for adding and updating product variants!)
  • If a new option values are added to the combinations of existing variants, the product variants should be generated by coping the existing ones.
  • It is not possible to add a product variant with a combination of option values with more or less option values as the existing product variants
  • The weightType determine if the weight is added to the product weight or replaces it.
  • The priceType determine if the price is added to the product price or replaces it.
  • The stockType determines the behavior for the stock management. Three options are available here:
  • only-positive: Only positive values are allowed as stock, including zero.
  • all-numbers: All numbers are allowed as stock.
  • not-managed: The stock is not actively managed, which means that this value is not considered for the determination of the current stock.
  • When removing an option from the product variant combinations, it's needed to provide a contained option value, that defines which product variants should be kept. The option is then removed completely from the combinations.

Domain events

Event Description
Gambio\Admin\Modules\ProductVariant\Model\Events\\ProductVariantCreated Will be raised if a new product variant has been created.
Gambio\Admin\Modules\ProductVariant\Model\Events\\ProductVariantDeleted Will be raised if an existing product variant has been removed.
Gambio\Admin\Modules\ProductVariant\Model\Events\\UpdatedProductVariantProductCustomization Will be raised if the product customizations of a product variant has been updated.
Gambio\Admin\Modules\ProductVariant\Model\Events\\UpdatedProductVariantProductIdentificationNumbers Will be raised if the product identification numbers of a product variant has been updated.
Gambio\Admin\Modules\ProductVariant\Model\Events\\UpdatedProductVariantsCombination Will be raised if the combination of a product variant has been updated.
Gambio\Admin\Modules\ProductVariant\Model\Events\\UpdatedProductVariantsImageListId Will be raised if the image list ID of a product variant has been updated.
Gambio\Admin\Modules\ProductVariant\Model\Events\\UpdatedProductVariantsSortOrder Will be raised if the sort order of a product variant has been updated.
Gambio\Admin\Modules\ProductVariant\Model\Events\\UpdatedProductVariantsStock Will be raised if the stock of a product variant has been updated.