Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SW-613-exported-main-variant-config #233

Merged
merged 17 commits into from
Jan 17, 2022
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# NEXT
- [SW-613] The configuration now has a new "Export" section, where you can choose which variant should be exported to Findologic as "main variant". Options are "Shopware default", "Main-/Parent product", "Cheapest variant".
- [SW-589] The dropdown for choosing the language in the configuration, will now only show languages that have a URL associated to them.
- [SW-672] Fixed a bug that caused the plugin to send too many requests to the Findologic API on category pages, which negatively impacted the performance on these pages.
- [SW-671] Fixed a bug that broke the build, due to usage of deprecated usage of Composer 1 classes.
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG_de-DE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# NEXT
- [SW-613] Die Konfiguration hat nun eine neue "Export" Sektion, wo ausgewählt werden kann, welche Variante als Hauptvariante exportiert werden soll. Auswahlmöglichkeiten sind "Shopware Standard", "Haupt-/Eltern Produkt", "Günstigste Variante".
- [SW-589] Das Dropdown zur Auswahl der Sprache in der Konfiguration, zeigt nun nur noch Sprachen an, die auch eine URL verknüpft haben.
- [SW-672] Ein Fehler wurde behoben, wodurch das Plugin auf Kategorieseiten zu viele Anfragen an die Findologic API gesendet hatte, wodurch die Performance auf diesen Seiten negativ beeinflusst wurde.
- [SW-671] Ein Fehler wurde behoben, wodurch der automatisierte build-Prozess scheiterte, da veraltete Composer 1 Klassen verwendet wurden.
Expand Down
7 changes: 6 additions & 1 deletion src/Controller/ExportController.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,13 @@ protected function initialize(Request $request, ?SalesChannelContext $context):
$this->salesChannelContext = $this->salesChannelService ? $this->salesChannelService
->getSalesChannelContext($context, $this->exportConfig->getShopkey()) : null;

$this->productService = ProductService::getInstance($this->container, $this->salesChannelContext);
$this->container->set('fin_search.sales_channel_context', $this->salesChannelContext);
$this->pluginConfig = $this->getPluginConfig();
$this->productService = ProductService::getInstance(
$this->container,
$this->salesChannelContext,
$this->pluginConfig
);

$this->export = Export::getInstance(
$this->exportConfig->getProductId() ? Export::TYPE_PRODUCT_ID : Export::TYPE_XML,
Expand All @@ -130,6 +134,7 @@ protected function validate(): ?Response
if (count($messages) > 0) {
$errorHandler = new ProductErrorHandler();
$errorHandler->getExportErrors()->addGeneralErrors($messages);

return $this->export->buildErrorResponse($errorHandler, $this->headerHandler->getHeaders());
}

Expand Down
117 changes: 112 additions & 5 deletions src/Export/ProductService.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

namespace FINDOLOGIC\FinSearch\Export;

use FINDOLOGIC\FinSearch\Findologic\MainVariant;
use FINDOLOGIC\FinSearch\Struct\Config;
use FINDOLOGIC\FinSearch\Utils\Utils;
use InvalidArgumentException;
use Psr\Container\ContainerInterface;
use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition;
use Shopware\Core\Content\Product\ProductCollection;
Expand All @@ -26,26 +29,34 @@ class ProductService
{
public const CONTAINER_ID = 'fin_search.product_service';

/** @var Config */
private $config;

/** @var ContainerInterface */
private $container;

/** @var SalesChannelContext|null */
private $salesChannelContext;

public function __construct(ContainerInterface $container, ?SalesChannelContext $salesChannelContext = null)
{
public function __construct(
ContainerInterface $container,
?SalesChannelContext $salesChannelContext = null,
?Config $config = null
) {
$this->container = $container;
$this->salesChannelContext = $salesChannelContext;
$this->config = $config ?? $container->get(Config::class);
}

public static function getInstance(
ContainerInterface $container,
?SalesChannelContext $salesChannelContext
?SalesChannelContext $salesChannelContext,
?Config $config = null
): ProductService {
if ($container->has(self::CONTAINER_ID)) {
$productService = $container->get(self::CONTAINER_ID);
} else {
$productService = new ProductService($container, $salesChannelContext);
$productService = new ProductService($container, $salesChannelContext, $config);
$container->set(self::CONTAINER_ID, $productService);
}

Expand All @@ -61,11 +72,21 @@ public function setSalesChannelContext(SalesChannelContext $salesChannelContext)
$this->salesChannelContext = $salesChannelContext;
}

public function setConfig(Config $config): void
{
$this->config = $config;
}

public function getSalesChannelContext(): ?SalesChannelContext
{
return $this->salesChannelContext;
}

public function getConfig(): ?Config
{
return $this->config;
}

public function getTotalProductCount(): int
{
$criteria = $this->buildProductCriteria();
Expand Down Expand Up @@ -275,7 +296,34 @@ protected function buildProductsWithVariantInformation(EntitySearchResult $resul
$products->add($product);
}

return $products;
if ($this->config->getMainVariant() === MainVariant::SHOPWARE_DEFAULT) {
return $products;
}

return $this->getProductsByMainVariantBasedOnConfig($products);
}

protected function getProductsByMainVariantBasedOnConfig(ProductCollection $products): ProductCollection
{
$mainVariantConfig = $this->config->getMainVariant();
$variantProducts = new ProductCollection();

foreach ($products as $product) {
switch ($mainVariantConfig) {
case MainVariant::MAIN_PARENT:
$parent = $this->getParentByMainProduct($product);
break;
case MainVariant::CHEAPEST:
$parent = $this->getParentByCheapestVariant($product);
break;
default:
throw new InvalidArgumentException($mainVariantConfig);
}

$variantProducts->add($parent);
}

return $variantProducts;
}

protected function getRealMainProductWithVariants(string $realMainProductId): ?ProductEntity
Expand All @@ -288,4 +336,63 @@ protected function assignChildrenOrSiblings(ProductEntity $product): void
$children = $this->getChildrenOrSiblings($product);
$product->setChildren($children);
}

protected function getParentByMainProduct(ProductEntity $product): ProductEntity
{
$parent = $product;
$children = new ProductCollection();
foreach ($product->getChildren() as $child) {
if ($child->getParentId()) {
$children->add($child);
} else {
$parent = $child;
}
}

$parent->setChildren($children);

return $parent;
}

protected function getParentByCheapestVariant(ProductEntity $product): ProductEntity
{
$currencyId = $this->salesChannelContext->getSalesChannel()->getCurrencyId();
$children = $product->getChildren();
// Add the current product in the children collection, so we can include it when
// checking for the cheapest price logic in the loop below.
$children->add($product);
// Get the real parent of the product. If no product is found, it means we
// already have the real parent.
$parent = $children->filter(static function (ProductEntity $childEntity) {
return $childEntity->getParentId() === null;
})->first();

if (!$parent) {
$parent = $product;
}

// Consider the current product to have the cheapest price by default, and look for
// a cheaper product in its children.
$cheapestPrice = $parent->getCurrencyPrice($currencyId);
foreach ($children as $child) {
$price = $child->getCurrencyPrice($currencyId);
if (!$price) {
continue;
}

if ($price->getGross() < $cheapestPrice->getGross()) {
$cheapestPrice->setGross($price->getGross());
$parent = $child;
}
}

$configuredChildren = $children->filter(static function (ProductEntity $child) use ($parent) {
return $child->getId() !== $parent->getId();
});

$parent->setParentId(null);
$parent->setChildren($configuredChildren);

return $parent;
}
}
12 changes: 12 additions & 0 deletions src/Findologic/MainVariant.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace FINDOLOGIC\FinSearch\Findologic;

final class MainVariant
{
public const SHOPWARE_DEFAULT = 'default';
public const MAIN_PARENT = 'parent';
public const CHEAPEST = 'cheapest';
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,6 @@
{{ $tc('findologic.settingForm.testButton') }}
</sw-button>
<span class="divider"></span>
<sw-multi-select
v-model="actualConfigData['FinSearch.config.crossSellingCategories']"
:options="categories"
:label="$tc('findologic.settingForm.config.crossSellingCategories.label')"
:helpText="$tc('findologic.settingForm.config.crossSellingCategories.tooltipText')"
:placeholder="$tc('findologic.settingForm.config.crossSellingCategories.placeholder')"
>
<template #selection-label-property="{ item }">
{{ item.name }}
</template>

</sw-multi-select>
<sw-text-field
v-model="actualConfigData['FinSearch.config.integrationType']"
ref="integrationType"
Expand All @@ -58,9 +46,37 @@
{% endblock %}
{% endblock %}
</sw-container>
</sw-card>


<sw-card class="sw-card--grid"
:title="$tc('findologic.settingForm.config.export.title')">
<sw-container>
<div class="findologic-settings-credentials-fields">
<sw-single-select
v-model="actualConfigData['FinSearch.config.mainVariant']"
:options="mainVariantOptions"
:label="$tc('findologic.settingForm.config.mainVariant.label')"
:placeholder="$tc('findologic.settingForm.config.mainVariant.default.label')"
:helpText="$tc('findologic.settingForm.config.mainVariant.tooltipText')">
</sw-single-select>

<sw-multi-select
v-model="actualConfigData['FinSearch.config.crossSellingCategories']"
:options="categories"
:label="$tc('findologic.settingForm.config.crossSellingCategories.label')"
:helpText="$tc('findologic.settingForm.config.crossSellingCategories.tooltipText')"
:placeholder="$tc('findologic.settingForm.config.crossSellingCategories.placeholder')"
>
<template #selection-label-property="{ item }">
{{ item.name }}
</template>
</sw-multi-select>
</div>
</sw-container>
</sw-card>


<sw-card class="sw-card--grid"
v-if="showDIConfig"
:title="$tc('findologic.settingForm.directIntegration.title')">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,23 @@ Component.register('findologic-config', {
}];
},

mainVariantOptions() {
return [
{
label: this.$tc('findologic.settingForm.config.mainVariant.default.label'),
value: 'default'
},
{
label: this.$tc('findologic.settingForm.config.mainVariant.parent.label'),
value: 'parent'
},
{
label: this.$tc('findologic.settingForm.config.mainVariant.cheapest.label'),
value: 'cheapest'
}
];
},

integrationType() {
return this.actualConfigData['FinSearch.config.integrationType'];
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ Component.register('findologic-page', {
if (!values['FinSearch.config.filterPosition']) {
values['FinSearch.config.filterPosition'] = 'top';
}
if (!values['FinSearch.config.mainVariant']) {
values['FinSearch.config.mainVariant'] = 'default';
}

this.actualConfigData = values;
this.isLoading = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,22 @@
"left": {
"label": "Links"
}
},
"export": {
"title": "Export"
},
"mainVariant": {
"label": "Exportierte Hauptvariante",
"tooltipText": "Wählen um die Variante, die auf Listing-Seiten angezeigt wird, zu ändern.",
"default": {
"label": "Shopware Standard"
},
"parent": {
"label": "Haupt-/Eltern Produkt"
},
"cheapest": {
"label": "Günstigste Variante"
}
}
},
"titleSuccess": "Erfolgreich",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,22 @@
"left": {
"label": "Left"
}
},
"export": {
"title": "Export"
},
"mainVariant": {
"label": "Exported main variant",
"tooltipText": "Select to change the variant that is shown on listing pages.",
"default": {
"label": "Shopware default"
},
"parent": {
"label": "Main-/Parent product"
},
"cheapest": {
"label": "Cheapest variant"
}
}
},
"titleSuccess": "Success",
Expand Down
2 changes: 1 addition & 1 deletion src/Resources/public/administration/js/fin-search.js

Large diffs are not rendered by default.

Loading