Skip to content

Commit

Permalink
Handle product query for multishop especially grouped stock, and add …
Browse files Browse the repository at this point in the history
…new integration test for multishop product list
  • Loading branch information
jolelievre committed Dec 14, 2022
1 parent cc351b3 commit 2f7625e
Show file tree
Hide file tree
Showing 5 changed files with 467 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/Adapter/Product/Update/ProductShopUpdater.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ private function copyStockToShop(ProductId $productId, ShopId $sourceShopId, Sho
$stockModification,
new OutOfStockType((int) $sourceStock->out_of_stock),
null,
$sourceStock->location,
$sourceStock->location
);
$this->productStockUpdater->update($productId, $stockProperties, ShopConstraint::shop($targetShopId->getValue()));
}
Expand Down
4 changes: 2 additions & 2 deletions src/Adapter/Shop/Repository/ShopGroupRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,12 @@ public function getShopGroupIdByShopId(ShopId $shopId): ShopGroupId
->setParameter('shopId', $shopId->getValue())
;

$result = $qb->execute()->fetchNumeric();
$result = $qb->execute()->fetchAssociative();
if (false === $result) {
throw new ShopNotFoundException(sprintf('Could not find shop with id %d', $shopId->getValue()));
}

return new ShopGroupId((int) $result);
return new ShopGroupId((int) $result['id_shop_group']);
}

/**
Expand Down
71 changes: 47 additions & 24 deletions src/Core/Grid/Query/ProductQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use PrestaShop\PrestaShop\Adapter\Configuration;
use PrestaShop\PrestaShop\Adapter\Shop\Repository\ShopGroupRepository;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopGroupId;
use PrestaShop\PrestaShop\Core\Exception\InvalidArgumentException;
use PrestaShop\PrestaShop\Core\Grid\Query\Filter\DoctrineFilterApplicatorInterface;
use PrestaShop\PrestaShop\Core\Grid\Query\Filter\SqlFilters;
use PrestaShop\PrestaShop\Core\Grid\Search\SearchCriteriaInterface;
Expand Down Expand Up @@ -147,19 +148,21 @@ public function getCountQueryBuilder(SearchCriteriaInterface $searchCriteria): Q
*/
private function getQueryBuilder(SearchCriteriaInterface $searchCriteria): QueryBuilder
{
$shopGroupId = null;
if (!$searchCriteria instanceof ShopSearchCriteriaInterface) {
throw new InvalidArgumentException(sprintf('Invalid search criteria, expected a %s', ShopSearchCriteriaInterface::class));
}

$shopId = null;
$groupSharedStock = false;
if ($searchCriteria instanceof ShopSearchCriteriaInterface) {
if ($searchCriteria->getShopConstraint()->getShopId()) {
$shopId = $searchCriteria->getShopConstraint()->getShopId()->getValue();
$shopGroup = $this->shopGroupRepository->getByShop($searchCriteria->getShopConstraint()->getShopId());
$groupSharedStock = (bool) $shopGroup->share_stock;
} elseif ($searchCriteria->getShopConstraint()->getShopGroupId()) {
$shopGroupId = $searchCriteria->getShopConstraint()->getShopGroupId()->getValue();
$shopGroup = $this->shopGroupRepository->get(new ShopGroupId($shopGroupId));
$groupSharedStock = (bool) $shopGroup->share_stock;
}
$filteredShopGroupId = null;
$sharedStockGroupId = null;
if ($searchCriteria->getShopConstraint()->getShopId()) {
$shopId = $searchCriteria->getShopConstraint()->getShopId()->getValue();
$shopGroup = $this->shopGroupRepository->getByShop($searchCriteria->getShopConstraint()->getShopId());
$sharedStockGroupId = (bool) $shopGroup->share_stock ? (int) $shopGroup->id : null;
} elseif ($searchCriteria->getShopConstraint()->getShopGroupId()) {
$filteredShopGroupId = $searchCriteria->getShopConstraint()->getShopGroupId()->getValue();
$shopGroup = $this->shopGroupRepository->get(new ShopGroupId($filteredShopGroupId));
$sharedStockGroupId = (bool) $shopGroup->share_stock ? $filteredShopGroupId : null;
}

$filterValues = $searchCriteria->getFilters();
Expand All @@ -170,33 +173,39 @@ private function getQueryBuilder(SearchCriteriaInterface $searchCriteria): Query
'p',
$this->dbPrefix . 'product_shop',
'ps',
'ps.`id_product` = p.`id_product` AND ps.`id_shop` = :shopId'
$this->addShopCondition('ps.`id_product` = p.`id_product`', 'ps', $shopId, $filteredShopGroupId)
)
->addOrderBy('ps.id_shop', 'ASC')
->leftJoin(
'p',
$this->dbPrefix . 'product_lang',
'pl',
'pl.`id_product` = p.`id_product` AND pl.`id_lang` = :langId AND pl.`id_shop` = :shopId'
$this->addShopCondition('pl.`id_product` = p.`id_product` AND pl.`id_lang` = :langId', 'pl', $shopId, $filteredShopGroupId)
)
->addOrderBy('pl.id_shop', 'ASC')
->leftJoin(
'ps',
$this->dbPrefix . 'category_lang',
'cl',
'cl.`id_category` = ps.`id_category_default` AND cl.`id_lang` = :langId AND cl.`id_shop` = :shopId'
$this->addShopCondition('cl.`id_category` = ps.`id_category_default` AND cl.`id_lang` = :langId', 'cl', $shopId, $filteredShopGroupId)
)
->addOrderBy('cl.id_shop', 'ASC')
->leftJoin(
'ps',
$this->dbPrefix . 'image_shop',
'img_shop',
'img_shop.`id_product` = ps.`id_product` AND img_shop.`cover` = 1 AND img_shop.`id_shop` = :shopId'
$this->addShopCondition('img_shop.`id_product` = ps.`id_product` AND img_shop.`cover` = 1', 'img_shop', $shopId, $filteredShopGroupId)
)
->addOrderBy('img_shop.id_shop', 'ASC')
->leftJoin(
'img_shop',
$this->dbPrefix . 'image_lang',
'img_lang',
'img_shop.`id_image` = img_lang.`id_image` AND img_lang.`id_lang` = :langId'
)
->andWhere('p.`state`=1')
->andWhere('p.`state` = 1')
// We group by product since we only need one row per product (there could be several ones if the product is associated to several shops)
->addGroupBy('p.id_product')
;

$filteredCategoryId = $this->getFilteredCategoryId($filterValues);
Expand All @@ -221,15 +230,13 @@ private function getQueryBuilder(SearchCriteriaInterface $searchCriteria): Query
AND sa.`id_product_attribute` = 0
';

if ($groupSharedStock && $shopGroupId) {
if ($sharedStockGroupId) {
$stockOnCondition .= '
AND sa.`id_shop` = 0 AND sa.`id_shop_group` = :shopGroupId
AND sa.`id_shop` = 0 AND sa.`id_shop_group` = :sharedShopGroupId
';
$qb->setParameter('shopGroupId', $shopGroupId);
$qb->setParameter('sharedShopGroupId', $sharedStockGroupId);
} else {
$stockOnCondition .= '
AND sa.`id_shop` = :shopId AND sa.`id_shop_group` = 0
';
$stockOnCondition = $this->addShopCondition($stockOnCondition, 'sa', $shopId, $filteredShopGroupId);
}

$qb->leftJoin(
Expand Down Expand Up @@ -278,8 +285,13 @@ private function getQueryBuilder(SearchCriteriaInterface $searchCriteria): Query
$this->filterApplicator->apply($qb, $sqlFilters, $filterValues);

// If shop is specified we use it as the reference, if not we use the product's default shop (for each product)
$qb->setParameter('shopId', $shopId ?? 'p.id_shop_default');
$qb->setParameter('langId', $this->contextLanguageId);
if ($shopId) {
$qb->setParameter('shopId', $shopId);
}
if ($filteredShopGroupId) {
$qb->setParameter('filteredShopGroupId', $filteredShopGroupId);
}

foreach ($filterValues as $filterName => $filter) {
if ('active' === $filterName) {
Expand Down Expand Up @@ -312,6 +324,17 @@ private function getQueryBuilder(SearchCriteriaInterface $searchCriteria): Query
return $qb;
}

private function addShopCondition(string $sql, string $tableAlias, ?int $shopId, ?int $filteredShopGroupId): string
{
if ($shopId) {
return $sql . ' AND ' . $tableAlias . '.`id_shop` = :shopId';
} elseif ($filteredShopGroupId) {
return $sql . ' AND ' . $tableAlias . '.`id_shop` IN (SELECT s2.id_shop FROM ' . $this->dbPrefix . 'shop s2 WHERE s2.id_shop_group = :filteredShopGroupId)';
}

return $sql . ' AND ' . $tableAlias . '.`id_shop` = p.id_shop_default';
}

private function getFilteredCategoryId(array $filterValues): ?int
{
foreach ($filterValues as $filterName => $filter) {
Expand Down
Loading

0 comments on commit 2f7625e

Please sign in to comment.