custom/plugins/SwagB2bPlatform/components/InStock/BridgePlatform/SalesChannelProductRepositoryDecorator.php line 85

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\B2B\InStock\BridgePlatform;
  3. use Shopware\B2B\Shop\BridgePlatform\SalesChannelProductExtension;
  4. use Shopware\B2B\StoreFrontAuthentication\Framework\AuthenticationService;
  5. use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\AggregationResultCollection;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\AntiJoinFilter;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\Filter;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\RangeFilter;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\IdSearchResult;
  17. use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
  18. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  19. use Shopware\Core\System\SystemConfig\SystemConfigService;
  20. use function end;
  21. use function explode;
  22. use function get_class;
  23. use function in_array;
  24. use function min;
  25. class SalesChannelProductRepositoryDecorator implements SalesChannelRepositoryInterface
  26. {
  27.     private const MAX_LINE_ITEM_QUANTITY_IN_CART_ROUTE 'core.cart.maxQuantity';
  28.     private const PRODUCT_IN_STOCKS_ID_FIELD 'product.b2bInStock.id';
  29.     private const PRODUCT_IN_STOCKS_AUTH_ID_FIELD 'product.b2bInStock.authId';
  30.     private const PRODUCT_IN_STOCKS_IN_STOCK_FIELD 'product.b2bInStock.inStock';
  31.     private const IN_STOCK_IN_STOCK_FIELD 'inStock';
  32.     private const IN_STOCK_AUTH_ID_FIELD 'authId';
  33.     private const PRODUCT_DEFINITION_STOCK_FIELDS = [
  34.         'availableStock',
  35.         'stock',
  36.     ];
  37.     /**
  38.      * @var SalesChannelRepositoryInterface
  39.      */
  40.     private $decorated;
  41.     /**
  42.      * @var AuthenticationService
  43.      */
  44.     private $authenticationService;
  45.     /**
  46.      * @var SystemConfigService
  47.      */
  48.     private $systemConfigService;
  49.     public function __construct(
  50.         SalesChannelRepositoryInterface $decorated,
  51.         AuthenticationService $authenticationService,
  52.         SystemConfigService $systemConfigService
  53.     ) {
  54.         $this->decorated $decorated;
  55.         $this->authenticationService $authenticationService;
  56.         $this->systemConfigService $systemConfigService;
  57.     }
  58.     public function search(Criteria $criteriaSalesChannelContext $salesChannelContext): EntitySearchResult
  59.     {
  60.         if (!$this->authenticationService->isB2b()) {
  61.             return $this->decorated->search($criteria$salesChannelContext);
  62.         }
  63.         $this->prepareCriteriaForB2bInStock($criteria);
  64.         $searchResult $this->decorated->search($criteria$salesChannelContext);
  65.         $searchResult $this->getUpdatedSearchResult($searchResult$salesChannelContext);
  66.         return $searchResult;
  67.     }
  68.     public function aggregate(Criteria $criteriaSalesChannelContext $salesChannelContext): AggregationResultCollection
  69.     {
  70.         return $this->decorated->aggregate($criteria$salesChannelContext);
  71.     }
  72.     public function searchIds(Criteria $criteriaSalesChannelContext $salesChannelContext): IdSearchResult
  73.     {
  74.         if (!$this->authenticationService->isB2b()) {
  75.             return $this->decorated->searchIds($criteria$salesChannelContext);
  76.         }
  77.         $this->prepareCriteriaForB2bInStock($criteria);
  78.         return $this->decorated->searchIds($criteria$salesChannelContext);
  79.     }
  80.     /**
  81.      * @internal
  82.      */
  83.     protected function prepareCriteriaForB2bInStock(Criteria $criteria): void
  84.     {
  85.         $filters $criteria->getFilters();
  86.         $criteria->addAssociation(SalesChannelProductExtension::B2B_IN_STOCK_EXTENSION_NAME);
  87.         $associationCriteria $criteria->getAssociation(SalesChannelProductExtension::B2B_IN_STOCK_EXTENSION_NAME);
  88.         $associationCriteria->addFilter(new EqualsAnyFilter(self::IN_STOCK_AUTH_ID_FIELD, [
  89.             $this->authenticationService->getIdentity()->getAuthId()->getValue(),
  90.             $this->authenticationService->getIdentity()->getContextAuthId()->getValue(),
  91.         ]));
  92.         $criteria->resetFilters();
  93.         foreach ($filters as $filter) {
  94.             $criteria->addFilter($this->mapFilter($filter));
  95.             if ($associationFilter $this->getAssociationFilter($filter)) {
  96.                 $associationCriteria->addFilter($associationFilter);
  97.             }
  98.         }
  99.     }
  100.     /**
  101.      * @internal
  102.      */
  103.     protected function mapFilter(Filter $filter): Filter
  104.     {
  105.         // ignore inherited MultiFilter
  106.         if (in_array(
  107.             $class get_class($filter),
  108.             [MultiFilter::class, NotFilter::class, AntiJoinFilter::class],
  109.             true
  110.         )) {
  111.             $queries = [];
  112.             foreach ($filter->getQueries() as $query) {
  113.                 $queries[] = $this->mapFilter($query);
  114.             }
  115.             return new $class($filter->getOperator(), $queries);
  116.         }
  117.         if (!$this->isInStockFilter($filter)) {
  118.             return $filter;
  119.         }
  120.         return $this->buildInStockFilter($filter);
  121.     }
  122.     /**
  123.      * @internal
  124.      */
  125.     protected function isInStockFilter(Filter $filter): bool
  126.     {
  127.         return ($filter instanceof EqualsFilter || $filter instanceof RangeFilter || $filter instanceof EqualsAnyFilter)
  128.             && in_array($this->getNecessaryFieldValue($filter->getField()), self::PRODUCT_DEFINITION_STOCK_FIELDStrue);
  129.     }
  130.     /**
  131.      * @internal
  132.      */
  133.     private function buildInStockFilter(Filter $filter): Filter
  134.     {
  135.         return new MultiFilter(
  136.             MultiFilter::CONNECTION_OR,
  137.             [
  138.                 new MultiFilter(MultiFilter::CONNECTION_AND, [
  139.                     $filter,
  140.                     new EqualsFilter(self::PRODUCT_IN_STOCKS_ID_FIELDnull),
  141.                 ]),
  142.                 new MultiFilter(MultiFilter::CONNECTION_AND, [
  143.                     $this->mapInStockFilter(self::PRODUCT_IN_STOCKS_IN_STOCK_FIELD$filter),
  144.                     new NotFilter(NotFilter::CONNECTION_AND, [new EqualsFilter(self::PRODUCT_IN_STOCKS_ID_FIELDnull)]),
  145.                     new EqualsAnyFilter(self::PRODUCT_IN_STOCKS_AUTH_ID_FIELD, [
  146.                         $this->authenticationService->getIdentity()->getAuthId()->getValue(),
  147.                         $this->authenticationService->getIdentity()->getContextAuthId()->getValue(),
  148.                     ]),
  149.                 ]),
  150.             ]
  151.         );
  152.     }
  153.     /**
  154.      * @internal
  155.      */
  156.     protected function mapInStockFilter(string $fieldNameFilter $filter): Filter
  157.     {
  158.         if ($filter instanceof RangeFilter) {
  159.             return new RangeFilter($fieldName$filter->getParameters());
  160.         }
  161.         if ($filter instanceof EqualsFilter) {
  162.             return new EqualsFilter($fieldName$filter->getValue());
  163.         }
  164.         if ($filter instanceof EqualsAnyFilter) {
  165.             return new EqualsAnyFilter($fieldName$filter->getValue());
  166.         }
  167.         return $filter;
  168.     }
  169.     /**
  170.      * @internal
  171.      */
  172.     protected function getAssociationFilter(Filter $filter): ?Filter
  173.     {
  174.         // ignore inherited MultiFilter
  175.         if (in_array(
  176.             $class get_class($filter),
  177.             [MultiFilter::class, NotFilter::class, AntiJoinFilter::class],
  178.             true
  179.         )) {
  180.             $queries = [];
  181.             foreach ($filter->getQueries() as $query) {
  182.                 $associationFilter $this->getAssociationFilter($query);
  183.                 if (!$associationFilter) {
  184.                     continue;
  185.                 }
  186.                 $queries[] = $associationFilter;
  187.             }
  188.             if (!$queries) {
  189.                 return null;
  190.             }
  191.             return new $class($filter->getOperator(), $queries);
  192.         }
  193.         if (!$this->isInStockFilter($filter)) {
  194.             return null;
  195.         }
  196.         return $this->mapInStockFilter(self::IN_STOCK_IN_STOCK_FIELD$filter);
  197.     }
  198.     /**
  199.      * @internal
  200.      */
  201.     protected function getUpdatedSearchResult(
  202.         EntitySearchResult $searchResult,
  203.         SalesChannelContext $salesChannelContext
  204.     ): EntitySearchResult {
  205.         $maxLineItemQuantityInCart = (int) $this->systemConfigService
  206.             ->get(self::MAX_LINE_ITEM_QUANTITY_IN_CART_ROUTE$salesChannelContext->getSalesChannel()->getId());
  207.         /** @var SalesChannelProductEntity $product */
  208.         foreach ($searchResult->getIterator() as $product) {
  209.             /** @var B2bInStockCollection $b2bInStocks */
  210.             if (!($b2bInStocks $product->getExtension(SalesChannelProductExtension::B2B_IN_STOCK_EXTENSION_NAME))
  211.                 || $b2bInStocks->count() === 0) {
  212.                 continue;
  213.             }
  214.             $inStock $b2bInStocks->getBestMatchByAuthId(
  215.                 $this->authenticationService->getIdentity()->getAuthId()
  216.             );
  217.             $product->setAvailableStock($inStock);
  218.             $product->setStock($inStock);
  219.             $product->setAvailable((bool) $inStock);
  220.             $product->setMaxPurchase(min($inStock$maxLineItemQuantityInCart));
  221.         }
  222.         return $searchResult;
  223.     }
  224.     /**
  225.      * @internal
  226.      */
  227.     protected function getNecessaryFieldValue(string $field): string
  228.     {
  229.         $explodedField explode('.'$field);
  230.         return end($explodedField);
  231.     }
  232. }