Skip to content

Commit 5f0b69b

Browse files
[framework] product recalculations priority queue (#2981)
2 parents caac8e1 + cf81b0b commit 5f0b69b

10 files changed

Lines changed: 136 additions & 33 deletions

src/Command/DispatchRecalculationMessageCommand.php

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
namespace Shopsys\FrameworkBundle\Command;
66

77
use Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationDispatcher;
8+
use Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnum;
9+
use Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnumInterface;
810
use Symfony\Component\Console\Attribute\AsCommand;
911
use Symfony\Component\Console\Command\Command;
1012
use Symfony\Component\Console\Input\InputArgument;
@@ -43,6 +45,13 @@ protected function configure(): void
4345
null,
4446
InputOption::VALUE_NONE,
4547
'Dispatch messages to recalculate all products',
48+
)
49+
->addOption(
50+
'priority',
51+
'p',
52+
InputOption::VALUE_OPTIONAL,
53+
sprintf('Define the message priority. Possible values are: %s', ProductRecalculationPriorityEnum::getPipeSeparatedValues()),
54+
ProductRecalculationPriorityEnum::REGULAR->value,
4655
);
4756
}
4857

@@ -55,18 +64,31 @@ protected function execute(InputInterface $input, OutputInterface $output): int
5564

5665
$productIds = $input->getArgument('productIds');
5766
$shouldRecalculateAll = $input->getOption('all');
67+
$priority = ProductRecalculationPriorityEnum::tryFrom($input->getOption('priority'));
68+
69+
if ($priority === null) {
70+
$symfonyStyle->error(sprintf('Invalid priority value. Possible values are: %s', ProductRecalculationPriorityEnum::getPipeSeparatedValues()));
71+
72+
return Command::FAILURE;
73+
}
5874

5975
if ($shouldRecalculateAll && count($productIds) > 0) {
6076
$symfonyStyle->error('You cannot use both `--all` and product IDs at the same time');
6177

6278
return Command::FAILURE;
6379
}
6480

81+
if ($shouldRecalculateAll && $priority === ProductRecalculationPriorityEnum::HIGH) {
82+
$symfonyStyle->error('Dispatching all products to the high priority queue is not supported');
83+
84+
return Command::FAILURE;
85+
}
86+
6587
if ($shouldRecalculateAll) {
6688
return $this->executeAll($symfonyStyle);
6789
}
6890

69-
return $this->executeIds($productIds, $symfonyStyle);
91+
return $this->executeIds($productIds, $symfonyStyle, $priority);
7092
}
7193

7294
/**
@@ -84,10 +106,14 @@ protected function executeAll(SymfonyStyle $symfonyStyle): int
84106
/**
85107
* @param int[] $productIds
86108
* @param \Symfony\Component\Console\Style\SymfonyStyle $symfonyStyle
109+
* @param \Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnum $priority
87110
* @return int
88111
*/
89-
protected function executeIds(array $productIds, SymfonyStyle $symfonyStyle): int
90-
{
112+
protected function executeIds(
113+
array $productIds,
114+
SymfonyStyle $symfonyStyle,
115+
ProductRecalculationPriorityEnumInterface $priority,
116+
): int {
91117
try {
92118
Assert::allNumeric($productIds, 'All product IDs must be numeric');
93119
Assert::notEmpty($productIds, 'You must specify at least one product ID');
@@ -97,8 +123,8 @@ protected function executeIds(array $productIds, SymfonyStyle $symfonyStyle): in
97123
return Command::FAILURE;
98124
}
99125

100-
$dispatchedProductIds = $this->productRecalculationDispatcher->dispatchProductIds($productIds);
101-
$symfonyStyle->success(['Dispatched message for IDs', implode(', ', $dispatchedProductIds)]);
126+
$dispatchedProductIds = $this->productRecalculationDispatcher->dispatchProductIds($productIds, $priority);
127+
$symfonyStyle->success(['Dispatched message for IDs', implode(', ', $dispatchedProductIds), sprintf('Priority: %s', $priority->value)]);
102128

103129
return Command::SUCCESS;
104130
}

src/Controller/Admin/ProductController.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828
use Shopsys\FrameworkBundle\Model\Product\ProductDataFactoryInterface;
2929
use Shopsys\FrameworkBundle\Model\Product\ProductFacade;
3030
use Shopsys\FrameworkBundle\Model\Product\ProductVariantFacade;
31+
use Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnum;
3132
use Shopsys\FrameworkBundle\Model\Product\Unit\UnitFacade;
3233
use Shopsys\FrameworkBundle\Twig\ProductExtension;
3334
use Symfony\Component\HttpFoundation\Request;
35+
use Symfony\Component\HttpFoundation\Response;
3436
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
3537
use Symfony\Component\Routing\Annotation\Route;
3638

@@ -74,8 +76,9 @@ public function __construct(
7476
* @Route("/product/edit/{id}", requirements={"id" = "\d+"})
7577
* @param \Symfony\Component\HttpFoundation\Request $request
7678
* @param int $id
79+
* @return \Symfony\Component\HttpFoundation\Response
7780
*/
78-
public function editAction(Request $request, int $id)
81+
public function editAction(Request $request, int $id): Response
7982
{
8083
$product = $this->productFacade->getById($id);
8184
$productData = $this->productDataFactory->createFromProduct($product);
@@ -85,7 +88,7 @@ public function editAction(Request $request, int $id)
8588

8689
if ($form->isSubmitted() && $form->isValid()) {
8790
$this->setSellingToUntilEndOfDay($productData);
88-
$this->productFacade->edit($id, $productData);
91+
$this->productFacade->edit($id, $productData, ProductRecalculationPriorityEnum::HIGH);
8992

9093
$this
9194
->addSuccessFlashTwig(
@@ -119,8 +122,9 @@ public function editAction(Request $request, int $id)
119122
/**
120123
* @Route("/product/new/")
121124
* @param \Symfony\Component\HttpFoundation\Request $request
125+
* @return \Symfony\Component\HttpFoundation\Response
122126
*/
123-
public function newAction(Request $request)
127+
public function newAction(Request $request): Response
124128
{
125129
try {
126130
$productData = $this->productDataFactory->create();
@@ -135,7 +139,7 @@ public function newAction(Request $request)
135139

136140
if ($form->isSubmitted() && $form->isValid()) {
137141
$this->setSellingToUntilEndOfDay($productData);
138-
$product = $this->productFacade->create($productData);
142+
$product = $this->productFacade->create($productData, ProductRecalculationPriorityEnum::HIGH);
139143

140144
$this
141145
->addSuccessFlashTwig(
@@ -226,13 +230,14 @@ public function listAction(Request $request)
226230
* @Route("/product/delete/{id}", requirements={"id" = "\d+"})
227231
* @CsrfProtection
228232
* @param int $id
233+
* @return \Symfony\Component\HttpFoundation\Response
229234
*/
230-
public function deleteAction($id)
235+
public function deleteAction(int $id): Response
231236
{
232237
try {
233238
$product = $this->productFacade->getById($id);
234239

235-
$this->productFacade->delete($id);
240+
$this->productFacade->delete($id, ProductRecalculationPriorityEnum::HIGH);
236241

237242
$this->addSuccessFlashTwig(
238243
t('Product <strong>{{ product|productDisplayName }}</strong> deleted'),

src/Model/Product/ProductFacade.php

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
use Shopsys\FrameworkBundle\Model\Product\Pricing\ProductPriceCalculation;
2222
use Shopsys\FrameworkBundle\Model\Product\Pricing\ProductSellingPrice;
2323
use Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationDispatcher;
24+
use Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnum;
25+
use Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnumInterface;
2426
use Shopsys\FrameworkBundle\Model\Stock\ProductStockData;
2527
use Shopsys\FrameworkBundle\Model\Stock\ProductStockFacade;
2628
use Shopsys\FrameworkBundle\Model\Stock\StockFacade;
@@ -84,10 +86,13 @@ public function getById($productId)
8486

8587
/**
8688
* @param \Shopsys\FrameworkBundle\Model\Product\ProductData $productData
89+
* @param \Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnum|null $priority nullable because of https://github.com/nikic/PHP-Parser/pull/940
8790
* @return \Shopsys\FrameworkBundle\Model\Product\Product
8891
*/
89-
public function create(ProductData $productData)
90-
{
92+
public function create(
93+
ProductData $productData,
94+
?ProductRecalculationPriorityEnumInterface $priority = null,
95+
): Product {
9196
$product = $this->productFactory->create($productData);
9297

9398
$this->em->persist($product);
@@ -98,7 +103,7 @@ public function create(ProductData $productData)
98103

99104
$this->editProductStockRelation($productData, $product);
100105

101-
$this->productRecalculationDispatcher->dispatchSingleProductId($product->getId());
106+
$this->productRecalculationDispatcher->dispatchSingleProductId($product->getId(), $priority ?? ProductRecalculationPriorityEnum::REGULAR);
102107

103108
return $product;
104109
}
@@ -131,10 +136,14 @@ public function setAdditionalDataAfterCreate(Product $product, ProductData $prod
131136
/**
132137
* @param int $productId
133138
* @param \Shopsys\FrameworkBundle\Model\Product\ProductData $productData
139+
* @param \Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnum|null $priority nullable because of https://github.com/nikic/PHP-Parser/pull/940
134140
* @return \Shopsys\FrameworkBundle\Model\Product\Product
135141
*/
136-
public function edit($productId, ProductData $productData)
137-
{
142+
public function edit(
143+
int $productId,
144+
ProductData $productData,
145+
?ProductRecalculationPriorityEnumInterface $priority = null,
146+
): Product {
138147
$product = $this->productRepository->getById($productId);
139148

140149
$productCategoryDomains = $this->productCategoryDomainFactory->createMultiple(
@@ -164,25 +173,28 @@ public function edit($productId, ProductData $productData)
164173

165174
$this->editProductStockRelation($productData, $product);
166175

167-
$this->productRecalculationDispatcher->dispatchSingleProductId($product->getId());
176+
$this->productRecalculationDispatcher->dispatchSingleProductId($product->getId(), $priority ?? ProductRecalculationPriorityEnum::REGULAR);
168177

169178
return $product;
170179
}
171180

172181
/**
173182
* @param int $productId
183+
* @param \Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnum|null $priority nullable because of https://github.com/nikic/PHP-Parser/pull/940
174184
*/
175-
public function delete($productId)
176-
{
185+
public function delete(
186+
int $productId,
187+
?ProductRecalculationPriorityEnumInterface $priority = null,
188+
): void {
177189
$product = $this->productRepository->getById($productId);
178190
$productDeleteResult = $product->getProductDeleteResult();
179191
$productsForRecalculations = $productDeleteResult->getProductsForRecalculations();
180192

181193
foreach ($productsForRecalculations as $productForRecalculations) {
182-
$this->productRecalculationDispatcher->dispatchSingleProductId($productForRecalculations->getId());
194+
$this->productRecalculationDispatcher->dispatchSingleProductId($productForRecalculations->getId(), $priority ?? ProductRecalculationPriorityEnum::REGULAR);
183195
}
184196

185-
$this->productRecalculationDispatcher->dispatchSingleProductId($product->getId());
197+
$this->productRecalculationDispatcher->dispatchSingleProductId($product->getId(), $priority ?? ProductRecalculationPriorityEnum::REGULAR);
186198

187199
$this->em->remove($product);
188200
$this->em->flush();

src/Model/Product/Recalculation/ProductRecalculationMessage.php renamed to src/Model/Product/Recalculation/AbstractProductRecalculationMessage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace Shopsys\FrameworkBundle\Model\Product\Recalculation;
66

7-
class ProductRecalculationMessage
7+
abstract class AbstractProductRecalculationMessage
88
{
99
/**
1010
* @param int $productId

src/Model/Product/Recalculation/ProductRecalculationDispatcher.php

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,50 @@ class ProductRecalculationDispatcher extends AbstractMessageDispatcher
1111
{
1212
/**
1313
* @param \Shopsys\FrameworkBundle\Model\Product\Product[] $products
14+
* @param \Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnum $productRecalculationPriorityEnum
1415
* @return int[]
1516
*/
16-
public function dispatchProducts(array $products): array
17-
{
17+
public function dispatchProducts(
18+
array $products,
19+
ProductRecalculationPriorityEnumInterface $productRecalculationPriorityEnum = ProductRecalculationPriorityEnum::REGULAR,
20+
): array {
1821
return $this->dispatchProductIds(
1922
array_map(static fn (Product $product) => $product->getId(), $products),
23+
$productRecalculationPriorityEnum,
2024
);
2125
}
2226

2327
/**
2428
* @param int[] $productIds
29+
* @param \Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnum $productRecalculationPriorityEnum
2530
* @return int[]
2631
*/
27-
public function dispatchProductIds(array $productIds): array
28-
{
32+
public function dispatchProductIds(
33+
array $productIds,
34+
ProductRecalculationPriorityEnumInterface $productRecalculationPriorityEnum = ProductRecalculationPriorityEnum::REGULAR,
35+
): array {
2936
$productIds = array_unique($productIds);
3037

3138
foreach ($productIds as $productId) {
32-
$this->messageBus->dispatch(new ProductRecalculationMessage((int)$productId));
39+
$message = match ($productRecalculationPriorityEnum) {
40+
ProductRecalculationPriorityEnum::HIGH => new ProductRecalculationPriorityHighMessage((int)$productId),
41+
ProductRecalculationPriorityEnum::REGULAR => new ProductRecalculationPriorityRegularMessage((int)$productId),
42+
};
43+
$this->messageBus->dispatch($message);
3344
}
3445

3546
return $productIds;
3647
}
3748

3849
/**
3950
* @param int $productId
51+
* @param \Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationPriorityEnum $productRecalculationPriorityEnum
4052
*/
41-
public function dispatchSingleProductId(int $productId): void
42-
{
43-
$this->dispatchProductIds([$productId]);
53+
public function dispatchSingleProductId(
54+
int $productId,
55+
ProductRecalculationPriorityEnumInterface $productRecalculationPriorityEnum = ProductRecalculationPriorityEnum::REGULAR,
56+
): void {
57+
$this->dispatchProductIds([$productId], $productRecalculationPriorityEnum);
4458
}
4559

4660
public function dispatchAllProducts(): void

src/Model/Product/Recalculation/ProductRecalculationMessageHandler.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ public function __construct(
2525
}
2626

2727
/**
28-
* @param \Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationMessage $message
28+
* @param \Shopsys\FrameworkBundle\Model\Product\Recalculation\AbstractProductRecalculationMessage $message
2929
* @param \Symfony\Component\Messenger\Handler\Acknowledger|null $ack
3030
* @return mixed
3131
*/
32-
public function __invoke(ProductRecalculationMessage $message, ?Acknowledger $ack = null): mixed
32+
public function __invoke(AbstractProductRecalculationMessage $message, ?Acknowledger $ack = null): mixed
3333
{
3434
return $this->handle($message, $ack);
3535
}
@@ -43,7 +43,7 @@ protected function process(array $jobs): void
4343
$acknowledgers = [];
4444

4545
/**
46-
* @var \Shopsys\FrameworkBundle\Model\Product\Recalculation\ProductRecalculationMessage $message
46+
* @var \Shopsys\FrameworkBundle\Model\Product\Recalculation\AbstractProductRecalculationMessage $message
4747
* @var \Symfony\Component\Messenger\Handler\Acknowledger $ack
4848
*/
4949
foreach ($jobs as [$message, $ack]) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Shopsys\FrameworkBundle\Model\Product\Recalculation;
6+
7+
enum ProductRecalculationPriorityEnum: string implements ProductRecalculationPriorityEnumInterface
8+
{
9+
case HIGH = 'high';
10+
case REGULAR = 'regular';
11+
12+
/**
13+
* @return string
14+
*/
15+
public static function getPipeSeparatedValues(): string
16+
{
17+
return implode('|', array_column(self::cases(), 'value'));
18+
}
19+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Shopsys\FrameworkBundle\Model\Product\Recalculation;
6+
7+
interface ProductRecalculationPriorityEnumInterface
8+
{
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Shopsys\FrameworkBundle\Model\Product\Recalculation;
6+
7+
class ProductRecalculationPriorityHighMessage extends AbstractProductRecalculationMessage
8+
{
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Shopsys\FrameworkBundle\Model\Product\Recalculation;
6+
7+
class ProductRecalculationPriorityRegularMessage extends AbstractProductRecalculationMessage
8+
{
9+
}

0 commit comments

Comments
 (0)