Product Cross-Selling
This section describes how the module maps product cross-selling to Shopware.
The data models of Shopware and synQup are very different regarding cross-selling. That's why this page provides some background information before giving you an example on how to create a cross-selling.
Quick Reference
- The field
Product::relations
is a collection that contains zero to many documents of typeRelation
. - Each
Relation
will be converted to aproduct_cross_selling
in Shopware. - Each
Relation
is identified by a combination of its parent product identifier and its position in theProduct::relations
collection - The field
Relation::products
contains references toProduct
documents - Each referenced
Product
of that relation will be assigned to the cross-selling in Shopware (= converted to an entry inproduct_cross_selling_assigned_products
) - There are two different types of cross-selling available in Shopware, but only the type
productList
(= "manual selection" in the Shopware backend) is supported by the module
Subsections
The following subsections are involved in mapping cross-selling:
output
└───product-cross-selling
└───product-cross-selling-build-cache
└───product-cross-selling-validate
└───product-cross-selling-upsert
Configuration
{
"subsections": {
"productCrossSelling": {
"enabled": true
}
}
}
Mapping Table
The actual source document is a Elio\CommerceBundle\Document\Product\Relation
that is part of the Product::relations
collection.
Target Field | Source Path |
---|---|
*T name |
label |
position | position |
active | active |
* productId |
mapped automatically - determined from the product of which the Relation was extracted from |
* type |
mapped automatically - static value productList (= "manual selection" in the Shopware backend) |
assignedProducts | products Each referenced Product of that relation will be assigned to the cross selling |
Cross-Selling in Shopware
- In Shopware every cross-selling is stored in the table
product_cross_selling
(andproduct_cross_selling_translation
). - The products assigned to that cross-selling are stored in the junction-table
product_cross_selling_assigned_products
. - There are two different types of cross-selling available in Shopware, but only the type
productList
(= "manual selection" in the Shopware backend) is supported by the module.
Cross-Selling in synQup
- The embedded document
Relation
is used to generateproduct_cross_sellings
in Shopware. - Every
Relation
that is part of theProduct::relations
collection will be converted to a cross-selling assigned to that specific product - All referenced products of that relation (see
Relation::products
collection) will be assigned to theproduct_cross_selling
.
Cross-Selling in the Output-Module
Due to differences of the data models the module has to perform several steps in order to map a Relation
to
a ProductCrossSellingEntity
:
- All
Relations
are extracted from their owningProducts
in theextract-embedded
subsections - Since a
Relation
does not provide an identifier the module has to generate custom identifiers (see next section) - The module searches the corresponding
product_cross_selling
entries in Shopware by the help of the previously generated identifier - The module converts the
Relation
toProductCrossSellingEntity
-objects and sends them to Shopware
Identifying Cross-Sellings
The source of a product_cross_selling
in Shopware is the document Relation
. However, the Relation
does not have an identifier.
That's why the module generates an identifier with the following pattern for every Relation
:
{product-identifier}-{collection-key* of Relation::products}}
*
the collection-key is the index/position in the collection
Example: The third Relation that is embedded in a product with the identifier "12345" gets the identifier 12345-3.
The assigned products are not identified at all. This means that the assigned products (= entries
of product_cross_selling_assigned_products
) are deleted and recreated on each update of the cross-selling.
Translations
There is a bug in Shopware 6 that prevents translatable cross-sellings from being created. Therefore, translations for the non-system language appear only after an update.
Example - How to Create a Cross-Selling
In this example we are going to generate three products:
- Product A (Notebook) is the product whose detail page should display a cross-selling
- Product B (Keyboard) and C (Mouse) are the products that should be shown as accessory articles on the page of Product A
class ProductCrossSellingsGenerator
{
public function generate(ModuleJobDispatchContext $context): void
{
// create products
$notebook = $this->createProduct('notebook', 'A very good notebook', 'Ein wirklich gutes Notebook', 2400);
$similarNotebook = $this->createProduct('similar-notebook', 'Another very good notebook', 'Ein anderes wirklich gutes Notebook', 2400);
$keyboard = $this->createProduct('keyboard', 'A pretty good keyboard', 'Eine ganz gute Tastatur', 15);
$mouse = $this->createProduct('mouse', 'A mouse', 'Eine Maus', 5);
// create cross sellings / relations
$accessoryItems = new Relation(
"accessory",
TranslationCollection::create([Locale::en_GB, 'Notebook Accessory Items'], [Locale::de_DE, 'Notebook Zubehör']),
new ArrayCollection([$keyboard, $mouse])
);
$similarItems = new Relation(
"similar",
TranslationCollection::create([Locale::en_GB, 'Similar Items'], [Locale::de_DE, 'Ähnliche Gegenstände']),
new ArrayCollection([$similarNotebook])
);
$notebook->setRelations(new ArrayCollection([$accessoryItems, $similarItems]));
// persist and flush
// ...
}
private function createProduct(string $identifier, string $englishName, string $germanName, int $netPrice): Product
{
$product = $this->findOrCreate($identifier);
$taxGroup = $this->documentManager->getRepository(TaxGroup::class)->findOneBy(['identifier' => 'default tax group']);
$product->setTaxGroup($taxGroup);
$name = TranslationCollection::create([Locale::en_GB, $englishName], [Locale::de_DE, $germanName]);
$product->setGeneralInformation(new GeneralInformation($name));
$product->setDefaultPrice(new SellingPrice($netPrice));
return $product;
}
}
This is the result in the Shopware administration panel: