Skip to content

Commit

Permalink
Handle new ShopCollection for combinations and a few other missing pl…
Browse files Browse the repository at this point in the history
…aces
  • Loading branch information
jolelievre committed Dec 19, 2024
1 parent 75a5794 commit b0273f2
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductId;
use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductType;
use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\ShopAssociationNotFound;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopCollection;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopId;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
Expand Down Expand Up @@ -166,7 +167,7 @@ private function syncOutOfStockType(
$productStockAvailable = $this->stockAvailableRepository->getForProduct($productId, new ShopId($product->getShopId()));
$outOfStockType = new OutOfStockType((int) $productStockAvailable->out_of_stock);

if ($shopConstraint->forAllShops()) {
if ($shopConstraint->forAllShops() || ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds())) {
foreach ($shopIdsByConstraint as $shopId) {
$this->combinationRepository->updateCombinationOutOfStockType($productId, $outOfStockType, ShopConstraint::shop($shopId->getValue()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

use Combination;
use Db;
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception;
use PrestaShop\PrestaShop\Adapter\Attribute\Repository\AttributeRepository;
Expand All @@ -49,6 +50,7 @@
use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductId;
use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\InvalidShopConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\ShopException;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopCollection;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopGroupId;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopId;
Expand Down Expand Up @@ -276,27 +278,44 @@ public function copyToShop(CombinationId $combinationId, ShopId $sourceId, ShopI
*/
public function getByShopConstraint(CombinationId $combinationId, ShopConstraint $shopConstraint): Combination
{
if ($shopConstraint->getShopGroupId()) {
throw new InvalidShopConstraintException('Combination has no features related with shop group use single shop and all shops constraints');
}

if ($shopConstraint->forAllShops()) {
if ($shopConstraint->forAllShops() || ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds())) {
try {
return $this->get($combinationId, $this->getDefaultShopIdForCombination($combinationId));
} catch (CombinationShopAssociationNotFoundException $e) {
// We try to fetch combination for default shop first,
// but in case it is not associated to default shop,
// then we load first found associated combination
} catch (CombinationShopAssociationNotFoundException $e) {
$associatedShopIds = $this->getAssociatedShopIds($combinationId);
if (empty($associatedShopIds)) {
throw $e;
}

return $this->get($combinationId, reset($associatedShopIds));
if ($shopConstraint instanceof ShopCollection) {
$defaultShopId = null;
// Find first shop IDs that is both in the specified list and the valid associated shops
$validShopIds = array_map(fn (ShopId $shopId) => $shopId->getValue(), $associatedShopIds);
foreach ($shopConstraint->getShopIds() as $shopId) {
if (in_array($shopId, $validShopIds)) {
$defaultShopId = $shopId;
break;
}
}

// If none is found, it means no provided shop IDs were associated so we trigger the exception
if (empty($defaultShopId)) {
throw $e;
}
} else {
$defaultShopId = reset($associatedShopIds);
}

return $this->get($combinationId, $defaultShopId);
}
} else {
} elseif ($shopConstraint->getShopId()) {
return $this->get($combinationId, $shopConstraint->getShopId());
}

throw new InvalidShopConstraintException('Combination has no features related with shop group use single shop, shop collection and all shops constraints');
}

/**
Expand All @@ -308,7 +327,7 @@ public function getByShopConstraint(CombinationId $combinationId, ShopConstraint
public function partialUpdate(Combination $combination, array $updatableProperties, ShopConstraint $shopConstraint, int $errorCode): void
{
if ($shopConstraint->getShopGroupId()) {
throw new InvalidShopConstraintException('Product combination has no features related with shop group use single shop and all shops constraints');
throw new InvalidShopConstraintException('Product Combination has no features related with shop group use single shop, shop collection and all shops constraints');
}

$this->combinationValidator->validate($combination);
Expand Down Expand Up @@ -459,11 +478,13 @@ function (array $combination) { return new CombinationId((int) $combination['id_
public function findFirstCombinationId(ProductId $productId, ShopConstraint $shopConstraint): ?CombinationId
{
if ($shopConstraint->getShopGroupId()) {
throw new InvalidShopConstraintException('Combination has no features related with shop group use single shop and all shops constraints');
throw new InvalidShopConstraintException('Combination has no features related with shop group use single shop, shop collection and all shops constraints');
}

if ($shopConstraint->getShopId()) {
$shopId = $shopConstraint->getShopId();
} elseif ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds()) {
$shopId = $shopConstraint->getShopIds()[0];
} else {
$shopId = $this->productRepository->getProductDefaultShopId($productId);
}
Expand Down Expand Up @@ -761,6 +782,10 @@ private function getShopIdsByConstraint(CombinationId $combinationId, ShopConstr
return $this->getAssociatedShopIds($combinationId);
}

if ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds()) {
return $shopConstraint->getShopIds();
}

return [$shopConstraint->getShopId()];
}

Expand Down Expand Up @@ -801,13 +826,29 @@ private function searchCombinationIdsByAttributes(
'pa',
'pac.id_product_attribute = pa.id_product_attribute'
);
} else {
} elseif ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds()) {
$qb
->innerJoin(
'pac',
$this->dbPrefix . 'product_attribute_shop',
'pa',
'pac.id_product_attribute = pa.id_product_attribute AND pa.id_shop IN (:shopIds)'
)
->setParameter(
'shopIds',
array_map(fn (ShopId $shopId) => $shopId->getValue(), $shopConstraint->getShopIds()),
ArrayParameterType::INTEGER
)
;
} elseif ($shopConstraint->getShopId()) {
$qb->innerJoin(
'pac',
$this->dbPrefix . 'product_attribute_shop',
'pa',
'pac.id_product_attribute = pa.id_product_attribute AND pa.id_shop = :shopId'
)->setParameter('shopId', $shopConstraint->getShopId()->getValue());
} else {
throw new InvalidShopConstraintException('Cannot handle this type of ShopConstraint');
}

$qb
Expand Down Expand Up @@ -870,12 +911,13 @@ private function searchAttributes(LanguageId $languageId, ShopConstraint $shopCo

if ($shopConstraint->getShopId()) {
// this makes sure we are searching only in certain shop, so it doesn't return irrelevant attribute ids
$qb->innerJoin(
'a',
$this->dbPrefix . 'attribute_shop',
'attrShop',
'a.id_attribute = attrShop.id_attribute AND attrShop.id_shop = :shopId'
)
$qb
->innerJoin(
'a',
$this->dbPrefix . 'attribute_shop',
'attrShop',
'a.id_attribute = attrShop.id_attribute AND attrShop.id_shop = :shopId'
)
->innerJoin(
'agl',
$this->dbPrefix . 'attribute_group_shop', 'ags',
Expand All @@ -885,6 +927,28 @@ private function searchAttributes(LanguageId $languageId, ShopConstraint $shopCo
;
}

if ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds()) {
// this makes sure we are searching only in certain shop, so it doesn't return irrelevant attribute ids
$qb
->innerJoin(
'a',
$this->dbPrefix . 'attribute_shop',
'attrShop',
'a.id_attribute = attrShop.id_attribute AND attrShop.id_shop IN (:shopIds)'
)
->innerJoin(
'agl',
$this->dbPrefix . 'attribute_group_shop', 'ags',
'agl.id_attribute_group = ags.id_attribute_group AND ags.id_shop IN (:shopIds)'
)
->setParameter(
'shopIds',
array_map(fn (ShopId $shopId) => $shopId->getValue(), $shopConstraint->getShopIds()),
ArrayParameterType::INTEGER
)
;
}

$results = $qb->executeQuery()->fetchAllAssociative();

return array_map('intval', array_column($results, 'id_attribute'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\ValueObject\CombinationId;
use PrestaShop\PrestaShop\Core\Domain\Product\Stock\ValueObject\StockId;
use PrestaShop\PrestaShop\Core\Domain\Product\Stock\ValueObject\StockModification;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopCollection;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopId;
use PrestaShop\PrestaShop\Core\Hook\HookDispatcherInterface;
Expand Down Expand Up @@ -189,11 +190,16 @@ private function updateStockByShopConstraint(
ShopConstraint $shopConstraint
): void {
$combinationId = new CombinationId((int) $combination->id);
if ($shopConstraint->forAllShops()) {
if ($shopConstraint->forAllShops() || ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds())) {
// Since each stock has a distinct ID we can't use the ObjectModel multi shop feature based on id_shop_list,
// so we manually loop to update each associated stocks
$shops = $this->combinationRepository->getAssociatedShopIds($combinationId);
foreach ($shops as $shopId) {
if ($shopConstraint instanceof ShopCollection) {
$shopIds = $shopConstraint->getShopIds();
} else {
$shopIds = $this->combinationRepository->getAssociatedShopIds($combinationId);
}

foreach ($shopIds as $shopId) {
$this->updateStockAvailable(
$this->stockAvailableRepository->getForCombination($combinationId, $shopId),
$properties
Expand Down
19 changes: 18 additions & 1 deletion src/Adapter/Product/Image/Repository/ProductImageRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ public function getImages(ProductId $productId, ShopConstraint $shopConstraint):
$this->productRepository->assertProductIsAssociatedToShop($productId, $shopConstraint->getShopId());
}

if ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds()) {
foreach ($shopConstraint->getShopIds() as $shopId) {
$this->productRepository->assertProductIsAssociatedToShop($productId, $shopId);
}
}

return array_map(
function (ImageId $imageId) use ($shopConstraint): Image {
return $this->getByShopConstraint($imageId, $shopConstraint);
Expand Down Expand Up @@ -168,11 +174,22 @@ public function getImageIds(ProductId $productId, ShopConstraint $shopConstraint
)
->setParameter('shopGroupId', $shopConstraint->getShopGroupId()->getValue())
;
} else {
} elseif ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds()) {
$qb
->andWhere('img_shop.id_shop IN (:shopIds)')
->setParameter(
'shopIds',
array_map(fn (ShopId $shopId) => $shopId->getValue(), $shopConstraint->getShopIds()),
ArrayParameterType::INTEGER
)
;
} elseif ($shopConstraint->getShopId()) {
$this->productRepository->assertProductIsAssociatedToShop($productId, $shopConstraint->getShopId());
$qb->andWhere('img_shop.id_shop = :shopId')
->setParameter('shopId', $shopConstraint->getShopId()->getValue())
;
} else {
throw new InvalidShopConstraintException('Cannot handle this type of ShopConstraint');
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public function __construct(
*/
public function getPackedProducts(PackId $productId, LanguageId $languageId, ShopConstraint $shopConstraint): array
{
if ($shopConstraint->getShopGroupId() || $shopConstraint->forAllShops()) {
if (!$shopConstraint->isSingleShopContext()) {
throw new InvalidShopConstraintException('Product Pack has no features related with shop group or all shops, use single shop constraint');
}

Expand Down
8 changes: 5 additions & 3 deletions src/Adapter/Product/Stock/Update/ProductStockUpdater.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,14 @@ public function resetStock(ProductId $productId, ShopConstraint $shopConstraint)
}

if ($shopConstraint->forAllShops()) {
$shops = $this->productRepository->getAssociatedShopIds($productId);
$shopIds = $this->productRepository->getAssociatedShopIds($productId);
} elseif ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds()) {
$shopIds = $shopConstraint->getShopIds();
} else {
$shops = [$shopConstraint->getShopId()];
$shopIds = [$shopConstraint->getShopId()];
}

foreach ($shops as $shopId) {
foreach ($shopIds as $shopId) {
$stockAvailable = $this->stockAvailableRepository->getForProduct($productId, $shopId);
if ((int) $stockAvailable->quantity === 0) {
continue;
Expand Down
39 changes: 31 additions & 8 deletions src/Adapter/Product/Update/ProductIndexationUpdater.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use PrestaShop\PrestaShop\Adapter\ContextStateManager;
use PrestaShop\PrestaShop\Core\Domain\Product\Exception\CannotUpdateProductException;
use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductVisibility;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopCollection;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use PrestaShopException;
Expand Down Expand Up @@ -102,12 +103,14 @@ public function updateIndexation(Product $product, ShopConstraint $shopConstrain
private function updateProductIndexes(int $productId, ShopConstraint $shopConstraint): void
{
try {
$this->adaptShopContext($shopConstraint);
if (!Search::indexation(false, $productId)) {
throw new CannotUpdateProductException(
sprintf('Cannot update search indexes for product %d', $productId),
CannotUpdateProductException::FAILED_UPDATE_SEARCH_INDEXATION
);
// If a specific list is provided we update them one by one
if ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds()) {
foreach ($shopConstraint->getShopIds() as $shopId) {
$this->updateProductIndexesByShopConstraint($productId, ShopConstraint::shop($shopId->getValue()));
}
} else {
// If not the other types of ShopConstraint are handled by this method
$this->updateProductIndexesByShopConstraint($productId, $shopConstraint);
}
} catch (PrestaShopException $e) {
throw new CoreException(
Expand All @@ -120,6 +123,17 @@ private function updateProductIndexes(int $productId, ShopConstraint $shopConstr
}
}

private function updateProductIndexesByShopConstraint(int $productId, ShopConstraint $shopConstraint): void
{
$this->adaptShopContext($shopConstraint);
if (!Search::indexation(false, $productId)) {
throw new CannotUpdateProductException(
sprintf('Cannot update search indexes for product %d', $productId),
CannotUpdateProductException::FAILED_UPDATE_SEARCH_INDEXATION
);
}
}

/**
* @param int $productId
* @param ShopConstraint $shopConstraint
Expand All @@ -129,8 +143,17 @@ private function updateProductIndexes(int $productId, ShopConstraint $shopConstr
private function removeProductIndexes(int $productId, ShopConstraint $shopConstraint): void
{
try {
$this->adaptShopContext($shopConstraint);
Search::removeProductsSearchIndex([$productId]);
// If a specific list is provided we update them one by one
if ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds()) {
foreach ($shopConstraint->getShopIds() as $shopId) {
$this->adaptShopContext(ShopConstraint::shop($shopId->getValue()));
Search::removeProductsSearchIndex([$productId]);
}
} else {
// If not the other types of ShopConstraint are handled by this method
$this->adaptShopContext($shopConstraint);
Search::removeProductsSearchIndex([$productId]);
}
} catch (PrestaShopException $e) {
throw new CoreException(
sprintf('Error occurred while removing search indexes for product %d', $productId),
Expand Down
14 changes: 14 additions & 0 deletions src/Core/Repository/ShopConstraintTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@

namespace PrestaShop\PrestaShop\Core\Repository;

use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Query\QueryBuilder;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopCollection;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopId;

trait ShopConstraintTrait
{
Expand All @@ -55,6 +58,17 @@ protected function applyShopConstraint(
;
}

if ($shopConstraint instanceof ShopCollection && $shopConstraint->hasShopIds()) {
$queryBuilder
->andWhere('id_shop IN (:shopIds)')
->setParameter(
'shopIds',
array_map(fn (ShopId $shopId) => $shopId->getValue(), $shopConstraint->getShopIds()),
ArrayParameterType::INTEGER
)
;
}

return $queryBuilder;
}
}

0 comments on commit b0273f2

Please sign in to comment.