<?php declare(strict_types=1);
namespace Shopware\B2B\OrderNumber\BridgePlatform;
use Shopware\B2B\Shop\BridgePlatform\SalesChannelProductExtension;
use Shopware\B2B\StoreFrontAuthentication\Framework\AuthenticationService;
use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\AggregationResultCollection;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\AntiJoinFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\Filter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\IdSearchResult;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use function end;
use function explode;
use function get_class;
use function in_array;
class SalesChannelProductRepositoryDecorator implements SalesChannelRepositoryInterface
{
private const ORDER_NUMBER_CONTEXT_OWNER_ID_FIELD = 'contextOwnerId';
private const ORDER_NUMBER_CUSTOM_ORDER_NUMBER_FIELD = 'customOrderNumber';
private const PRODUCT_DEFINITION_PRODUCT_NUMBER_FIELD = 'productNumber';
private const PRODUCT_ORDER_NUMBER_ID_FIELD = 'product.b2bOrderNumber.id';
private const PRODUCT_ORDER_NUMBER_CUSTOM_ORDER_NUMBER_FIELD = 'product.b2bOrderNumber.customOrderNumber';
private const PRODUCT_ORDER_NUMBER_CONTEXT_OWNER_ID_FIELD = 'product.b2bOrderNumber.contextOwnerId';
/**
* @var AuthenticationService
*/
private $authenticationService;
/**
* @var SalesChannelRepositoryInterface
*/
private $decorated;
public function __construct(
AuthenticationService $authenticationService,
SalesChannelRepositoryInterface $decorated
) {
$this->authenticationService = $authenticationService;
$this->decorated = $decorated;
}
public function search(Criteria $criteria, SalesChannelContext $salesChannelContext): EntitySearchResult
{
if (!$this->authenticationService->isB2b()) {
return $this->decorated->search($criteria, $salesChannelContext);
}
$this->prepareCriteriaForB2bOrderNumber($criteria);
return $this->decorated->search($criteria, $salesChannelContext);
}
public function aggregate(Criteria $criteria, SalesChannelContext $salesChannelContext): AggregationResultCollection
{
return $this->decorated->aggregate($criteria, $salesChannelContext);
}
public function searchIds(Criteria $criteria, SalesChannelContext $salesChannelContext): IdSearchResult
{
if (!$this->authenticationService->isB2b()) {
return $this->decorated->searchIds($criteria, $salesChannelContext);
}
$this->prepareCriteriaForB2bOrderNumber($criteria);
return $this->decorated->searchIds($criteria, $salesChannelContext);
}
/**
* @internal
*/
protected function prepareCriteriaForB2bOrderNumber(Criteria $criteria): void
{
$filters = $criteria->getFilters();
$criteria->addAssociation(SalesChannelProductExtension::B2B_ORDER_NUMBER_EXTENSION_NAME);
$associationCriteria = $criteria->getAssociation(SalesChannelProductExtension::B2B_ORDER_NUMBER_EXTENSION_NAME);
$associationCriteria->addFilter(new EqualsFilter(
self::ORDER_NUMBER_CONTEXT_OWNER_ID_FIELD,
$this->authenticationService->getIdentity()->getContextAuthId()->getValue()
));
$criteria->resetFilters();
foreach ($filters as $filter) {
$criteria->addFilter($this->mapFilter($filter));
}
}
/**
* @internal
*/
protected function mapFilter(Filter $filter): Filter
{
// ignore inherited MultiFilter
if (in_array(
$class = get_class($filter),
[MultiFilter::class, NotFilter::class, AntiJoinFilter::class],
true
)) {
$queries = [];
foreach ($filter->getQueries() as $query) {
$queries[] = $this->mapFilter($query);
}
return new $class($filter->getOperator(), $queries);
}
if (!$this->isOrderNumberFilter($filter)) {
return $filter;
}
return $this->buildOrderNumberFilter($filter);
}
/**
* @internal
*/
protected function isOrderNumberFilter(Filter $filter): bool
{
return ($filter instanceof EqualsFilter || $filter instanceof EqualsAnyFilter || $filter instanceof ContainsFilter)
&& $this->getNecessaryFieldValue($filter->getField()) === self::PRODUCT_DEFINITION_PRODUCT_NUMBER_FIELD;
}
/**
* @internal
*/
protected function buildOrderNumberFilter(Filter $filter): Filter
{
return new MultiFilter(
MultiFilter::CONNECTION_OR,
[
$filter,
new MultiFilter(MultiFilter::CONNECTION_AND, [
$this->mapOrderNumberFilter(self::PRODUCT_ORDER_NUMBER_CUSTOM_ORDER_NUMBER_FIELD, $filter),
new NotFilter(NotFilter::CONNECTION_AND, [new EqualsFilter(self::PRODUCT_ORDER_NUMBER_ID_FIELD, null)]),
new EqualsFilter(self::PRODUCT_ORDER_NUMBER_CONTEXT_OWNER_ID_FIELD, $this->authenticationService->getIdentity()->getContextAuthId()->getValue()),
]),
]
);
}
/**
* @internal
*/
protected function mapOrderNumberFilter(string $fieldName, Filter $filter): Filter
{
if ($filter instanceof EqualsFilter) {
return new EqualsFilter($fieldName, $filter->getValue());
}
if ($filter instanceof EqualsAnyFilter) {
return new EqualsAnyFilter($fieldName, $filter->getValue());
}
if ($filter instanceof ContainsFilter) {
return new ContainsFilter($fieldName, $filter->getValue());
}
return $filter;
}
/**
* @internal
*/
protected function getNecessaryFieldValue(string $field): string
{
$explodedField = explode('.', $field);
return end($explodedField);
}
}