diff --git a/config/services/synchronizer.xml b/config/services/synchronizer.xml
index 2956669..5352ab1 100644
--- a/config/services/synchronizer.xml
+++ b/config/services/synchronizer.xml
@@ -13,6 +13,7 @@
+ %commerce_weavers_sylius_also_bought.batch_size_limit%
diff --git a/docs/installation.md b/docs/installation.md
index ac3046a..0eca3eb 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -24,6 +24,7 @@ imports:
commerce_weavers_sylius_also_bought:
number_of_products_to_associate: 10 # default value
+ batch_size_limit: 1000 # default value
```
### 4. Add trait to enhance Sylius Product model
diff --git a/src/DependencyInjection/CommerceWeaversSyliusAlsoBoughtExtension.php b/src/DependencyInjection/CommerceWeaversSyliusAlsoBoughtExtension.php
index d94501a..950bdf2 100644
--- a/src/DependencyInjection/CommerceWeaversSyliusAlsoBoughtExtension.php
+++ b/src/DependencyInjection/CommerceWeaversSyliusAlsoBoughtExtension.php
@@ -42,6 +42,11 @@ public function prepend(ContainerBuilder $container): void
$config['number_of_products_to_associate'],
);
+ $container->setParameter(
+ 'commerce_weavers_sylius_also_bought.batch_size_limit',
+ $config['batch_size_limit'],
+ );
+
$this->prependDoctrineMigrations($container);
$this->prependDoctrineMapping($container);
}
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index 7e45c7a..99812df 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -32,6 +32,7 @@ private function addConfiguration(ArrayNodeDefinition $node): void
$node
->children()
->integerNode('number_of_products_to_associate')->defaultValue(10)->end()
+ ->integerNode('batch_size_limit')->defaultValue(1000)->end()
->end()
;
}
diff --git a/src/Provider/PlacedOrdersProvider.php b/src/Provider/PlacedOrdersProvider.php
index 524d1bf..7820875 100644
--- a/src/Provider/PlacedOrdersProvider.php
+++ b/src/Provider/PlacedOrdersProvider.php
@@ -13,13 +13,15 @@ public function __construct(private OrderRepositoryInterface $orderRepository)
{
}
- public function getSince(\DateTimeInterface $since): array
+ public function getSince(\DateTimeInterface $since, int $limit, int $offset): array
{
/** @var OrderInterface[] $result */
$result = $this->orderRepository
->createListQueryBuilder()
->andWhere('o.checkoutCompletedAt >= :date')
->setParameter('date', $since)
+ ->setMaxResults($limit)
+ ->setFirstResult($offset)
->getQuery()
->getResult()
;
diff --git a/src/Provider/PlacedOrdersProviderInterface.php b/src/Provider/PlacedOrdersProviderInterface.php
index 687c3a8..c525087 100644
--- a/src/Provider/PlacedOrdersProviderInterface.php
+++ b/src/Provider/PlacedOrdersProviderInterface.php
@@ -9,5 +9,5 @@
interface PlacedOrdersProviderInterface
{
/** @return OrderInterface[] */
- public function getSince(\DateTimeInterface $since): array;
+ public function getSince(\DateTimeInterface $since, int $limit, int $offset): array;
}
diff --git a/src/Synchronizer/FrequentlyBoughtTogetherProductsSynchronizer.php b/src/Synchronizer/FrequentlyBoughtTogetherProductsSynchronizer.php
index 0be8a2a..e7bb7d1 100644
--- a/src/Synchronizer/FrequentlyBoughtTogetherProductsSynchronizer.php
+++ b/src/Synchronizer/FrequentlyBoughtTogetherProductsSynchronizer.php
@@ -8,6 +8,7 @@
use CommerceWeavers\SyliusAlsoBoughtPlugin\Model\SynchronizationResult;
use CommerceWeavers\SyliusAlsoBoughtPlugin\Provider\PlacedOrdersProviderInterface;
use CommerceWeavers\SyliusAlsoBoughtPlugin\Saver\BoughtTogetherProductsInfoSaverInterface;
+use Sylius\Component\Core\Model\OrderInterface;
final class FrequentlyBoughtTogetherProductsSynchronizer implements FrequentlyBoughtTogetherProductsSynchronizerInterface
{
@@ -15,13 +16,38 @@ public function __construct(
private PlacedOrdersProviderInterface $placedOrdersProvider,
private BoughtTogetherProductsMapperInterface $boughtTogetherProductsMapper,
private BoughtTogetherProductsInfoSaverInterface $boughtTogetherProductsInfoSaver,
+ private int $batchSize = 1000,
) {
}
public function synchronize(\DateTimeInterface $since): SynchronizationResult
{
- $orders = $this->placedOrdersProvider->getSince($since);
+ $offset = 0;
+ $numberOfOrders = 0;
+ $affectedProducts = [];
+
+ while (($orders = $this->placedOrdersProvider->getSince($since, $this->batchSize, $offset)) !== []) {
+ $affectedProducts = array_merge($affectedProducts, $this->getAffectedProducts($orders));
+ $numberOfOrders += count($orders);
+
+ $offset += $this->batchSize;
+ }
+
+ foreach ($affectedProducts as $productCode => $products) {
+ $this->boughtTogetherProductsInfoSaver->save($productCode, $products);
+ }
+
+ return new SynchronizationResult($numberOfOrders, array_keys($affectedProducts));
+ }
+
+ /**
+ * @param OrderInterface[] $orders
+ *
+ * @return array>
+ */
+ private function getAffectedProducts(array $orders): array
+ {
$affectedProducts = [];
foreach ($orders as $order) {
@@ -32,10 +58,6 @@ public function synchronize(\DateTimeInterface $since): SynchronizationResult
}
}
- foreach ($affectedProducts as $productCode => $products) {
- $this->boughtTogetherProductsInfoSaver->save($productCode, $products);
- }
-
- return new SynchronizationResult(count($orders), array_keys($affectedProducts));
+ return $affectedProducts;
}
}
diff --git a/tests/Unit/DependencyInjection/ConfigurationTest.php b/tests/Unit/DependencyInjection/ConfigurationTest.php
index 1ebaa81..fc2399d 100644
--- a/tests/Unit/DependencyInjection/ConfigurationTest.php
+++ b/tests/Unit/DependencyInjection/ConfigurationTest.php
@@ -31,6 +31,25 @@ public function it_allows_to_define_number_of_products_to_associate(): void
'number_of_products_to_associate'
);
}
+ /** @test */
+ public function it_defines_batch_size_limit_by_default(): void
+ {
+ $this->assertProcessedConfigurationEquals(
+ [],
+ ['batch_size_limit' => 1000],
+ 'batch_size_limit'
+ );
+ }
+
+ /** @test */
+ public function it_allows_to_define_batch_size_limit(): void
+ {
+ $this->assertProcessedConfigurationEquals(
+ [['batch_size_limit' => 5]],
+ ['batch_size_limit' => 5],
+ 'batch_size_limit'
+ );
+ }
protected function getConfiguration(): Configuration
{
diff --git a/tests/Unit/Provider/PlacedOrdersProviderTest.php b/tests/Unit/Provider/PlacedOrdersProviderTest.php
index 7333276..26750b5 100644
--- a/tests/Unit/Provider/PlacedOrdersProviderTest.php
+++ b/tests/Unit/Provider/PlacedOrdersProviderTest.php
@@ -29,13 +29,15 @@ public function testItProvidesOrdersPlacedSinceSpecificDate(): void
$orderRepository->createListQueryBuilder()->willReturn($queryBuilder->reveal())->shouldBeCalled();
$queryBuilder->andWhere('o.checkoutCompletedAt >= :date')->willReturn($queryBuilder->reveal())->shouldBeCalled();
+ $queryBuilder->setMaxResults(10)->willReturn($queryBuilder->reveal())->shouldBeCalled();
+ $queryBuilder->setFirstResult(0)->willReturn($queryBuilder->reveal())->shouldBeCalled();
$queryBuilder->setParameter('date', $since)->willReturn($queryBuilder->reveal())->shouldBeCalled();
$queryBuilder->getQuery()->willReturn($query->reveal())->shouldBeCalled();
$query->getResult()->willReturn([$firstOrder, $secondOrder]);
self::assertSame(
[$firstOrder, $secondOrder],
- (new PlacedOrdersProvider($orderRepository->reveal()))->getSince($since)
+ (new PlacedOrdersProvider($orderRepository->reveal()))->getSince($since, 10, 0)
);
}
}
diff --git a/tests/Unit/Synchronizer/FrequentlyBoughtTogetherProductsSynchronizerTest.php b/tests/Unit/Synchronizer/FrequentlyBoughtTogetherProductsSynchronizerTest.php
index cb9b899..f99dc1f 100644
--- a/tests/Unit/Synchronizer/FrequentlyBoughtTogetherProductsSynchronizerTest.php
+++ b/tests/Unit/Synchronizer/FrequentlyBoughtTogetherProductsSynchronizerTest.php
@@ -25,27 +25,41 @@ public function testProductsSynchronization(): void
$firstOrder = new Order();
$secondOrder = new Order();
+ $thirdOrder = new Order();
$synchronizer = new FrequentlyBoughtTogetherProductsSynchronizer(
$placedOrdersProvider->reveal(),
$productsMapper->reveal(),
$boughtTogetherProductsInfoSaver->reveal(),
+ 2,
);
$placedOrdersProvider
- ->getSince(new \DateTimeImmutable('2020-01-01'))
+ ->getSince(new \DateTimeImmutable('2020-01-01'), 2, 0)
->willReturn([$firstOrder, $secondOrder])
;
+ $placedOrdersProvider
+ ->getSince(new \DateTimeImmutable('2020-01-01'), 2, 2)
+ ->willReturn([$thirdOrder])
+ ;
+
+ $placedOrdersProvider
+ ->getSince(new \DateTimeImmutable('2020-01-01'), 2, 4)
+ ->willReturn([])
+ ;
+
$productsMapper->map($firstOrder)->willReturn(['P10273' => ['P12182', 'P12183'], 'P12182' => ['P10273', 'P12183'], 'P12183' => ['P10273', 'P12182']]);
$productsMapper->map($secondOrder)->willReturn(['P12182' => ['P10273'], 'P10273' => ['P12182']]);
+ $productsMapper->map($thirdOrder)->willReturn(['P13757' => ['P10273']]);
$boughtTogetherProductsInfoSaver->save('P10273', ['P12182', 'P12183', 'P12182'])->shouldBeCalled();
$boughtTogetherProductsInfoSaver->save('P12182', ['P10273', 'P12183', 'P10273'])->shouldBeCalled();
$boughtTogetherProductsInfoSaver->save('P12183', ['P10273', 'P12182'])->shouldBeCalled();
+ $boughtTogetherProductsInfoSaver->save('P13757', ['P10273'])->shouldBeCalled();
self::assertEquals(
- new SynchronizationResult(2, ['P10273', 'P12182', 'P12183']),
+ new SynchronizationResult(3, ['P10273', 'P12182', 'P12183', 'P13757']),
$synchronizer->synchronize(new \DateTimeImmutable('2020-01-01')),
);
}