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::relationsis a collection that contains zero to many documents of typeRelation. - Each
Relationwill be converted to aproduct_cross_sellingin Shopware. - Each
Relationis identified by a combination of its parent product identifier and its position in theProduct::relationscollection - The field
Relation::productscontains references toProductdocuments - Each referenced
Productof 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
Relationis used to generateproduct_cross_sellingsin Shopware. - Every
Relationthat is part of theProduct::relationscollection will be converted to a cross-selling assigned to that specific product - All referenced products of that relation (see
Relation::productscollection) 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
Relationsare extracted from their owningProductsin theextract-embeddedsubsections - Since a
Relationdoes not provide an identifier the module has to generate custom identifiers (see next section) - The module searches the corresponding
product_cross_sellingentries in Shopware by the help of the previously generated identifier - The module converts the
RelationtoProductCrossSellingEntity-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:

