diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 98f3514..8226b6e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -64,10 +64,6 @@ jobs:
name: Run PHPStan
run: vendor/bin/phpstan analyse -c phpstan.neon -l max src/
- -
- name: Run Psalm
- run: vendor/bin/psalm
-
-
name: Run PHPSpec
run: vendor/bin/phpspec run --ansi -f progress --no-interaction
@@ -151,10 +147,6 @@ jobs:
name: Run PHPStan
run: vendor/bin/phpstan analyse -c phpstan.neon -l max src/
- -
- name: Run Psalm
- run: vendor/bin/psalm
-
-
name: Run PHPSpec
run: vendor/bin/phpspec run --ansi -f progress --no-interaction
diff --git a/composer.json b/composer.json
index 6aaead7..d2ef34c 100644
--- a/composer.json
+++ b/composer.json
@@ -44,8 +44,7 @@
"symfony/dotenv": "^5.4 || ^6.0",
"symfony/flex": "^2.2.2",
"symfony/intl": "^5.4 || ^6.0",
- "symfony/web-profiler-bundle": "^5.4 || ^6.0",
- "vimeo/psalm": "5.25.0"
+ "symfony/web-profiler-bundle": "^5.4 || ^6.0"
},
"conflict": {
"behat/mink-selenium2-driver": ">=1.7.0"
@@ -94,6 +93,15 @@
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd",
"security-checker security:check": "script"
- }
+ },
+ "analyse": [
+ "composer validate --ansi --strict",
+ "vendor/bin/phpstan analyse -c phpstan.neon -l max src/"
+ ],
+ "test": [
+ "vendor/bin/phpspec run --ansi -f progress --no-interaction",
+ "vendor/bin/phpunit --colors=always",
+ "vendor/bin/behat --colors --strict -vvv --no-interaction || vendor/bin/behat --colors --strict -vvv --no-interaction --rerun"
+ ]
}
}
diff --git a/config/app/config.yaml b/config/app/config.yaml
index 71be874..e1de9ac 100644
--- a/config/app/config.yaml
+++ b/config/app/config.yaml
@@ -5,3 +5,8 @@ sylius_ui:
commerce_weavers_sylius_also_bought_missing_configuration:
template: '@CommerceWeaversSyliusAlsoBoughtPlugin/_missing_configuration.html.twig'
priority: 40
+ sylius.admin.channel.form.second_column_content:
+ blocks:
+ commerce_weavers_sylius_also_bought_number_of_synchronised_products:
+ template: '@CommerceWeaversSyliusAlsoBoughtPlugin/Admin/Channel/Form/numberOfSynchronisedProducts.html.twig'
+ priority: 0
diff --git a/config/services/form.xml b/config/services/form.xml
new file mode 100644
index 0000000..30b3a5b
--- /dev/null
+++ b/config/services/form.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/config/services/processor.xml b/config/services/processor.xml
index b665763..0db467e 100644
--- a/config/services/processor.xml
+++ b/config/services/processor.xml
@@ -10,7 +10,7 @@
- %commerce_weavers_sylius_also_bought.number_of_products_to_associate%
+
diff --git a/config/services/provider.xml b/config/services/provider.xml
index b0d7e98..2b3a926 100644
--- a/config/services/provider.xml
+++ b/config/services/provider.xml
@@ -27,5 +27,12 @@
>
+
+
+ %commerce_weavers_sylius_also_bought.number_of_products_to_associate%
+
diff --git a/features/managing_number_of_items_to_synchronize_on_channel.feature b/features/managing_number_of_items_to_synchronize_on_channel.feature
new file mode 100644
index 0000000..29e6fc5
--- /dev/null
+++ b/features/managing_number_of_items_to_synchronize_on_channel.feature
@@ -0,0 +1,22 @@
+@managing_channels
+Feature: Managing number of items to synchronize on channel
+ In order to change the number of items that are synchronised as bought together products
+ As an Administrator
+ I want to be able to edit their number on channel configuration
+
+ Background:
+ Given the store operates on a channel named "Web Channel"
+ And I am logged in as an administrator
+
+ @ui
+ Scenario: Seeing the default number of synchronised products
+ When I want to modify a channel "Web Channel"
+ Then the number of synchronised products should be 10
+
+ @ui
+ Scenario: Modifying the number of synchronised products
+ When I want to modify a channel "Web Channel"
+ And I change the number of synchronised products to 5
+ And I save my changes
+ Then I should be notified that it has been successfully edited
+ And the number of synchronised products should be 5
diff --git a/features/synchronizing_bought_together_products.feature b/features/synchronizing_bought_together_products.feature
index eac6089..4f49cb5 100644
--- a/features/synchronizing_bought_together_products.feature
+++ b/features/synchronizing_bought_together_products.feature
@@ -41,6 +41,39 @@ Feature: Synchronizing bought together products
And the "Grains" product should have usually been bought with "Twigs", "Bird bath", "Bird netting", "Weaver" and "Swan"
And the "Bird feeder" product should have usually been bought with "Grains", "Twigs", "Bird bath", "Bird netting" and "Swift"
+ @cli
+ Scenario: Synchronizing maximum number of bought together products
+ Given the channel "United States" has 2 configured as number of synchronizable bought together products
+ And the store has a product association type "Bought together" with a code "bought_together"
+ And the store has products "Weaver", "Swan", "Swift", "Grains", "Twigs", "Bird feeder", "Bird bath" and "Bird netting"
+ And there is a customer "john.doe@example.com" that placed an order
+ And the customer bought a single "Weaver", "Bird bath" and "Bird netting"
+ And the customer bought 100 "Grains" products
+ And the customer bought 500 "Twigs" products
+ And the customer "John Doe" addressed it to "Elm Street", "90802" "Anytown" in the "United States" with identical billing address
+ And the customer chose "Free" shipping method with "Cash on delivery" payment
+ And this order is already paid
+ And there is another customer "john.galt@example.com" that placed an order
+ And the customer bought a single "Swan"
+ And the customer bought 200 "Grains" products
+ And the customer bought 20 "Twigs" products
+ And the customer "John Galt" addressed it to "Atlas Way", "385" "Libertyville" in the "United States" with identical billing address
+ And the customer chose "Free" shipping method with "Cash on delivery" payment
+ And this order is already paid
+ And there is another customer "jane.doe@example.com" that placed an order
+ And the customer bought a single "Swift", "Bird feeder", "Bird bath" and "Bird netting"
+ And the customer bought 100 "Grains" products
+ And the customer bought 100 "Twigs" products
+ And the customer "Jane Doe" addressed it to "Elm Street", "90802" "Anytown" in the "United States" with identical billing address
+ And the customer chose "Free" shipping method with "Cash on delivery" payment
+ And this order is already paid
+ When I synchronize bought together products by running command
+ Then I should be informed that the bought together products are synchronized
+ And the "Weaver" product should have usually been bought with "Grains" and "Twigs"
+ And the "Swan" product should have usually been bought with "Grains" and "Twigs"
+ And the "Grains" product should have usually been bought with "Twigs" and "Bird bath"
+ And the "Bird feeder" product should have usually been bought with "Grains" and "Twigs"
+
@cli
Scenario: Being notified about specific association type requirement
Given the store has a product "Weaver"
diff --git a/migrations/Version20240408131819.php b/migrations/Version20240408131819.php
new file mode 100644
index 0000000..202f6c0
--- /dev/null
+++ b/migrations/Version20240408131819.php
@@ -0,0 +1,26 @@
+addSql('ALTER TABLE sylius_channel ADD number_of_synchronised_products INT DEFAULT 10 NOT NULL');
+ }
+
+ public function down(Schema $schema): void
+ {
+ $this->addSql('ALTER TABLE sylius_channel DROP number_of_synchronised_products');
+ }
+}
diff --git a/phpstan.neon b/phpstan.neon
index 5d15c5a..181059c 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -6,7 +6,7 @@ parameters:
- src
- tests/Behat
- excludes_analyse:
+ excludePaths:
# Makes PHPStan crash
- 'src/DependencyInjection/Configuration.php'
diff --git a/psalm.xml b/psalm.xml
deleted file mode 100644
index 9aeae74..0000000
--- a/psalm.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/DependencyInjection/CommerceWeaversSyliusAlsoBoughtExtension.php b/src/DependencyInjection/CommerceWeaversSyliusAlsoBoughtExtension.php
index 950bdf2..ed66978 100644
--- a/src/DependencyInjection/CommerceWeaversSyliusAlsoBoughtExtension.php
+++ b/src/DependencyInjection/CommerceWeaversSyliusAlsoBoughtExtension.php
@@ -18,7 +18,6 @@ final class CommerceWeaversSyliusAlsoBoughtExtension extends AbstractResourceExt
private const ALIAS = 'commerce_weavers_sylius_also_bought';
- /** @psalm-suppress UnusedVariable */
public function load(array $configs, ContainerBuilder $container): void
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../../config'));
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index aec7c25..72def83 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -15,7 +15,6 @@
final class Configuration implements ConfigurationInterface
{
- /** @psalm-suppress UnusedVariable */
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('commerce_weavers_sylius_also_bought');
diff --git a/src/Entity/BoughtTogetherConfigurableChannelInterface.php b/src/Entity/BoughtTogetherConfigurableChannelInterface.php
new file mode 100644
index 0000000..0ba99f0
--- /dev/null
+++ b/src/Entity/BoughtTogetherConfigurableChannelInterface.php
@@ -0,0 +1,12 @@
+ 10])]
+ private int $numberOfSynchronisedProducts = 10;
+
+ public function setNumberOfSynchronisedProducts(int $number): void
+ {
+ $this->numberOfSynchronisedProducts = $number;
+ }
+
+ public function getNumberOfSynchronisedProducts(): int
+ {
+ return $this->numberOfSynchronisedProducts;
+ }
+}
diff --git a/src/Form/Extension/ChannelTypeExtension.php b/src/Form/Extension/ChannelTypeExtension.php
new file mode 100644
index 0000000..7ab21c9
--- /dev/null
+++ b/src/Form/Extension/ChannelTypeExtension.php
@@ -0,0 +1,28 @@
+add('numberOfSynchronisedProducts', NumberType::class, [
+ 'label' => 'commerce_weavers_sylius_also_bought.form.channel.number_of_synchronised_products',
+ 'required' => true,
+ ])
+ ;
+ }
+
+ public static function getExtendedTypes(): iterable
+ {
+ yield ChannelType::class;
+ }
+}
diff --git a/src/Processor/BoughtTogetherProductsAssociationProcessor.php b/src/Processor/BoughtTogetherProductsAssociationProcessor.php
index 4e4490b..6da3172 100644
--- a/src/Processor/BoughtTogetherProductsAssociationProcessor.php
+++ b/src/Processor/BoughtTogetherProductsAssociationProcessor.php
@@ -7,6 +7,7 @@
use CommerceWeavers\SyliusAlsoBoughtPlugin\Entity\BoughtTogetherProductsAwareInterface;
use CommerceWeavers\SyliusAlsoBoughtPlugin\Event\SynchronizationEnded;
use CommerceWeavers\SyliusAlsoBoughtPlugin\Provider\BoughtTogetherProductsAssociationProviderInterface;
+use CommerceWeavers\SyliusAlsoBoughtPlugin\Provider\SynchronizableProductsNumberProviderInterface;
use Doctrine\ORM\EntityManagerInterface;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Core\Repository\ProductRepositoryInterface;
@@ -20,7 +21,8 @@ public function __construct(
private EntityManagerInterface $entityManager,
private ProductRepositoryInterface $productRepository,
private BoughtTogetherProductsAssociationProviderInterface $boughtTogetherProductsAssociationProvider,
- private int $numberOfProductsToAssociate = 10,
+ private SynchronizableProductsNumberProviderInterface $synchronizableProductsNumberProvider,
+ private int $batchSize = 100,
) {
}
@@ -31,8 +33,14 @@ public function __invoke(SynchronizationEnded $event): void
Assert::allIsInstanceOf($products, BoughtTogetherProductsAwareInterface::class);
+ $batch = 0;
foreach ($products as $product) {
- $boughtTogetherProducts = array_slice($product->getBoughtTogetherProducts(), 0, $this->numberOfProductsToAssociate - 1, true);
+ $boughtTogetherProducts = array_slice(
+ $product->getBoughtTogetherProducts(),
+ 0,
+ $this->synchronizableProductsNumberProvider->getNumberOfProductsToSynchronise($product) - 1,
+ true,
+ );
$boughtTogetherAssociation = $this->boughtTogetherProductsAssociationProvider->getForProduct($product);
$this->entityManager->persist($boughtTogetherAssociation);
@@ -42,14 +50,17 @@ public function __invoke(SynchronizationEnded $event): void
foreach ($frequentlyBoughtTogetherProducts as $frequentlyBoughtTogetherProduct) {
$boughtTogetherAssociation->addAssociatedProduct($frequentlyBoughtTogetherProduct);
}
+
+ if (++$batch >= $this->batchSize) {
+ $this->entityManager->flush();
+ $batch = 0;
+ }
}
$this->entityManager->flush();
}
- /**
- * @return ProductInterface[]
- */
+ /** @return ProductInterface[] */
private function findProductsByCodes(array $codes): array
{
/** @var ProductInterface[] $products */
diff --git a/src/Provider/SynchronizableProductsNumberProvider.php b/src/Provider/SynchronizableProductsNumberProvider.php
new file mode 100644
index 0000000..3ab6e40
--- /dev/null
+++ b/src/Provider/SynchronizableProductsNumberProvider.php
@@ -0,0 +1,32 @@
+getChannels();
+
+ $numberOfProducts = 0;
+ /** @var BoughtTogetherConfigurableChannelInterface $channel */
+ foreach ($channels as $channel) {
+ $numberOfProducts = max($numberOfProducts, $channel->getNumberOfSynchronisedProducts());
+ }
+
+ if ($numberOfProducts <= 0) {
+ return $this->defaultNumberOfProducts;
+ }
+
+ return $numberOfProducts;
+ }
+}
diff --git a/src/Provider/SynchronizableProductsNumberProviderInterface.php b/src/Provider/SynchronizableProductsNumberProviderInterface.php
new file mode 100644
index 0000000..d088aa4
--- /dev/null
+++ b/src/Provider/SynchronizableProductsNumberProviderInterface.php
@@ -0,0 +1,12 @@
+
+
+ {{ form_row(form.numberOfSynchronisedProducts) }}
+
diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml
index 4646286..5c4ac11 100644
--- a/tests/Application/config/packages/_sylius.yaml
+++ b/tests/Application/config/packages/_sylius.yaml
@@ -6,7 +6,7 @@ imports:
- { resource: "@SyliusShopBundle/Resources/config/app/config.yml" }
- { resource: "@SyliusApiBundle/Resources/config/app/config.yaml" }
-
+
- { resource: "@CommerceWeaversSyliusAlsoBoughtPlugin/config/app/config.yaml" }
parameters:
@@ -18,9 +18,15 @@ sylius_shop:
sylius_api:
enabled: true
-
+
sylius_product:
resources:
product:
classes:
model: Tests\CommerceWeavers\SyliusAlsoBoughtPlugin\Application\Entity\Product
+
+sylius_channel:
+ resources:
+ channel:
+ classes:
+ model: Tests\CommerceWeavers\SyliusAlsoBoughtPlugin\Application\Entity\Channel
diff --git a/tests/Application/src/Entity/Channel.php b/tests/Application/src/Entity/Channel.php
new file mode 100644
index 0000000..b2e63e2
--- /dev/null
+++ b/tests/Application/src/Entity/Channel.php
@@ -0,0 +1,17 @@
+setNumberOfSynchronisedProducts($number);
+
+ $this->entityManager->flush();
+ }
+}
diff --git a/tests/Behat/Context/Ui/Admin/ManagingChannelsContext.php b/tests/Behat/Context/Ui/Admin/ManagingChannelsContext.php
new file mode 100644
index 0000000..c544142
--- /dev/null
+++ b/tests/Behat/Context/Ui/Admin/ManagingChannelsContext.php
@@ -0,0 +1,32 @@
+channelFormElement->changeNumberOfSynchronisedProducts($number);
+ }
+
+ /**
+ * @Then the number of synchronised products should be :numner
+ */
+ public function theNumberOfSynchronisedProductsShouldBe(int $number): void
+ {
+ Assert::same($number, $this->channelFormElement->getNumberOfSynchronisedProducts());
+ }
+}
diff --git a/tests/Behat/Element/Admin/ChannelFormElement.php b/tests/Behat/Element/Admin/ChannelFormElement.php
new file mode 100644
index 0000000..385372d
--- /dev/null
+++ b/tests/Behat/Element/Admin/ChannelFormElement.php
@@ -0,0 +1,27 @@
+getElement('number_of_synchronised_products_input')->setValue($number);
+ }
+
+ public function getNumberOfSynchronisedProducts(): int
+ {
+ return (int) $this->getElement('number_of_synchronised_products_input')->getValue();
+ }
+
+ protected function getDefinedElements(): array
+ {
+ return array_merge(parent::getDefinedElements(), [
+ 'number_of_synchronised_products_input' => 'input[name="sylius_channel[numberOfSynchronisedProducts]"]',
+ ]);
+ }
+}
diff --git a/tests/Behat/Element/Admin/ChannelFormElementInterface.php b/tests/Behat/Element/Admin/ChannelFormElementInterface.php
new file mode 100644
index 0000000..538d539
--- /dev/null
+++ b/tests/Behat/Element/Admin/ChannelFormElementInterface.php
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/tests/Behat/Resources/services/contexts/ui.xml b/tests/Behat/Resources/services/contexts/ui.xml
index 1845ac0..17ef289 100644
--- a/tests/Behat/Resources/services/contexts/ui.xml
+++ b/tests/Behat/Resources/services/contexts/ui.xml
@@ -4,6 +4,10 @@
+
+
+
+
diff --git a/tests/Behat/Resources/services/elements/admin.xml b/tests/Behat/Resources/services/elements/admin.xml
index 57423a2..c76714c 100644
--- a/tests/Behat/Resources/services/elements/admin.xml
+++ b/tests/Behat/Resources/services/elements/admin.xml
@@ -7,5 +7,11 @@
class="Tests\CommerceWeavers\SyliusAlsoBoughtPlugin\Behat\Element\Admin\MissingConfigurationErrorMessageElement"
parent="sylius.behat.element"
/>
+
+
diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml
index a8ffb99..235522b 100644
--- a/tests/Behat/Resources/suites.yml
+++ b/tests/Behat/Resources/suites.yml
@@ -1,5 +1,6 @@
imports:
- suites/api/viewing_products.yaml
- suites/cli/also_bought.yaml
+ - suites/ui/managing_channels.yaml
- suites/ui/viewing_error_message.yaml
- suites/ui/viewing_products.yaml
diff --git a/tests/Behat/Resources/suites/cli/also_bought.yaml b/tests/Behat/Resources/suites/cli/also_bought.yaml
index 7df0f08..3424012 100644
--- a/tests/Behat/Resources/suites/cli/also_bought.yaml
+++ b/tests/Behat/Resources/suites/cli/also_bought.yaml
@@ -3,17 +3,18 @@ default:
cli_also_bought:
contexts:
- sylius.behat.context.hook.doctrine_orm
-
+
- sylius.behat.context.setup.channel
- sylius.behat.context.setup.geographical
- sylius.behat.context.setup.order
- sylius.behat.context.setup.payment
- sylius.behat.context.setup.product
- - sylius.behat.context.setup.product_association
+ - sylius.behat.context.setup.product_association
- sylius.behat.context.setup.shipping
+ - Tests\CommerceWeavers\SyliusAlsoBoughtPlugin\Behat\Context\Setup\ChannelContext
- Tests\CommerceWeavers\SyliusAlsoBoughtPlugin\Behat\Context\Setup\OrderContext
- Tests\CommerceWeavers\SyliusAlsoBoughtPlugin\Behat\Context\Setup\ProductContext
-
+
- sylius.behat.context.transform.address
- sylius.behat.context.transform.channel
- sylius.behat.context.transform.customer
@@ -22,7 +23,7 @@ default:
- sylius.behat.context.transform.shared_storage
- sylius.behat.context.transform.shipping_method
- Tests\CommerceWeavers\SyliusAlsoBoughtPlugin\Behat\Context\Transform\ProductContext
-
+
- Tests\CommerceWeavers\SyliusAlsoBoughtPlugin\Behat\Context\Cli\CreateBoughtTogetherProductAssociationTypeContext
- Tests\CommerceWeavers\SyliusAlsoBoughtPlugin\Behat\Context\Cli\SynchronizeBoughtTogetherProductsContext
filters:
diff --git a/tests/Behat/Resources/suites/ui/managing_channels.yaml b/tests/Behat/Resources/suites/ui/managing_channels.yaml
new file mode 100644
index 0000000..b581b31
--- /dev/null
+++ b/tests/Behat/Resources/suites/ui/managing_channels.yaml
@@ -0,0 +1,33 @@
+default:
+ suites:
+ ui_managing_channels:
+ contexts:
+ - sylius.behat.context.hook.doctrine_orm
+ - sylius.behat.context.hook.session
+
+ - sylius.behat.context.transform.address
+ - sylius.behat.context.transform.channel
+ - sylius.behat.context.transform.country
+ - sylius.behat.context.transform.currency
+ - sylius.behat.context.transform.locale
+ - sylius.behat.context.transform.shared_storage
+ - sylius.behat.context.transform.taxon
+ - sylius.behat.context.transform.zone
+
+ - sylius.behat.context.setup.channel
+ - sylius.behat.context.setup.currency
+ - sylius.behat.context.setup.geographical
+ - sylius.behat.context.setup.locale
+ - sylius.behat.context.setup.payment
+ - sylius.behat.context.setup.admin_security
+ - sylius.behat.context.setup.shipping
+ - sylius.behat.context.setup.taxonomy
+ - sylius.behat.context.setup.zone
+
+ - sylius.behat.context.ui.admin.managing_channels
+ - sylius.behat.context.ui.admin.managing_channels_billing_data
+ - sylius.behat.context.ui.admin.notification
+
+ - Tests\CommerceWeavers\SyliusAlsoBoughtPlugin\Behat\Context\Ui\Admin\ManagingChannelsContext
+ filters:
+ tags: "@managing_channels&&@ui"
diff --git a/tests/Unit/Checker/BoughtTogetherProductAssociationTypeConfiguredCheckerTest.php b/tests/Unit/Checker/BoughtTogetherProductAssociationTypeConfiguredCheckerTest.php
index 1f9e30f..ae443d0 100644
--- a/tests/Unit/Checker/BoughtTogetherProductAssociationTypeConfiguredCheckerTest.php
+++ b/tests/Unit/Checker/BoughtTogetherProductAssociationTypeConfiguredCheckerTest.php
@@ -33,7 +33,7 @@ public function testItChecksIfBoughtTogetherProductAssociationTypeIsConfigured(b
self::assertSame($isConfigured, $checker->isConfigured());
}
- public function isConfiguredDataProvider(): array
+ public static function isConfiguredDataProvider(): array
{
return [
'association type is configured' => [
diff --git a/tests/Unit/Processor/BoughtTogetherProductsAssociationProcessorTest.php b/tests/Unit/Processor/BoughtTogetherProductsAssociationProcessorTest.php
index b90b573..fa09cc5 100644
--- a/tests/Unit/Processor/BoughtTogetherProductsAssociationProcessorTest.php
+++ b/tests/Unit/Processor/BoughtTogetherProductsAssociationProcessorTest.php
@@ -7,6 +7,7 @@
use CommerceWeavers\SyliusAlsoBoughtPlugin\Event\SynchronizationEnded;
use CommerceWeavers\SyliusAlsoBoughtPlugin\Processor\BoughtTogetherProductsAssociationProcessor;
use CommerceWeavers\SyliusAlsoBoughtPlugin\Provider\BoughtTogetherProductsAssociationProviderInterface;
+use CommerceWeavers\SyliusAlsoBoughtPlugin\Provider\SynchronizableProductsNumberProviderInterface;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
@@ -24,12 +25,13 @@ public function testProcessingBoughtTogetherProductsIntoAssociation(): void
$entityManager = $this->prophesize(EntityManagerInterface::class);
$productRepository = $this->prophesize(ProductRepositoryInterface::class);
$boughtTogetherProductsAssociationProvider = $this->prophesize(BoughtTogetherProductsAssociationProviderInterface::class);
+ $numberOfSynchronizableProductsProvider = $this->prophesize(SynchronizableProductsNumberProviderInterface::class);
$processor = new BoughtTogetherProductsAssociationProcessor(
$entityManager->reveal(),
$productRepository->reveal(),
$boughtTogetherProductsAssociationProvider->reveal(),
- 5
+ $numberOfSynchronizableProductsProvider->reveal(),
);
$product = $this->prophesize(Product::class);
@@ -38,6 +40,8 @@ public function testProcessingBoughtTogetherProductsIntoAssociation(): void
$boughtTogetherProductsAssociationProvider->getForProduct($product->reveal())->willReturn($productAssociation);
$product->getBoughtTogetherProducts()->willReturn(['12345' => 2, '500' => 1]);
+ $numberOfSynchronizableProductsProvider->getNumberOfProductsToSynchronise($product->reveal())->willReturn(10);
+
$firstAssociatedProduct = new Product();
$firstAssociatedProduct->setCode('12345');
$secondAssociatedProduct = new Product();
@@ -50,6 +54,66 @@ public function testProcessingBoughtTogetherProductsIntoAssociation(): void
$productAssociation->addAssociatedProduct($firstAssociatedProduct)->shouldBeCalled();
$productAssociation->addAssociatedProduct($secondAssociatedProduct)->shouldBeCalled();
+ $entityManager->persist($productAssociation)->shouldBeCalled();
+ $entityManager->flush()->shouldBeCalledOnce();
+
$processor(new SynchronizationEnded(Uuid::v4(), new \DateTimeImmutable(), 10, ['10137']));
}
+
+ public function testItFlushesProgressInBatches(): void
+ {
+ $entityManager = $this->prophesize(EntityManagerInterface::class);
+ $productRepository = $this->prophesize(ProductRepositoryInterface::class);
+ $boughtTogetherProductsAssociationProvider = $this->prophesize(BoughtTogetherProductsAssociationProviderInterface::class);
+ $numberOfSynchronizableProductsProvider = $this->prophesize(SynchronizableProductsNumberProviderInterface::class);
+
+ $processor = new BoughtTogetherProductsAssociationProcessor(
+ $entityManager->reveal(),
+ $productRepository->reveal(),
+ $boughtTogetherProductsAssociationProvider->reveal(),
+ $numberOfSynchronizableProductsProvider->reveal(),
+ 1,
+ );
+
+ $firstProduct = $this->prophesize(Product::class);
+ $secondProduct = $this->prophesize(Product::class);
+ $firstProductAssociation = $this->prophesize(ProductAssociation::class);
+ $secondProductAssociation = $this->prophesize(ProductAssociation::class);
+
+ $boughtTogetherProductsAssociationProvider->getForProduct($firstProduct->reveal())->willReturn($firstProductAssociation);
+ $firstProduct->getBoughtTogetherProducts()->willReturn(['12345' => 2, '500' => 1]);
+
+ $boughtTogetherProductsAssociationProvider->getForProduct($secondProduct->reveal())->willReturn($secondProductAssociation);
+ $secondProduct->getBoughtTogetherProducts()->willReturn(['10' => 2, '20' => 1]);
+
+ $numberOfSynchronizableProductsProvider->getNumberOfProductsToSynchronise($firstProduct->reveal())->willReturn(10);
+ $numberOfSynchronizableProductsProvider->getNumberOfProductsToSynchronise($secondProduct->reveal())->willReturn(10);
+
+ $firstAssociatedProduct = new Product();
+ $firstAssociatedProduct->setCode('12345');
+ $secondAssociatedProduct = new Product();
+ $secondAssociatedProduct->setCode('500');
+ $thirdAssociatedProduct = new Product();
+ $thirdAssociatedProduct->setCode('10');
+ $fourthAssociatedProduct = new Product();
+ $fourthAssociatedProduct->setCode('20');
+
+ $productRepository->findBy(['code' => ['10137', '12345']])->willReturn([$firstProduct, $secondProduct]);
+ $productRepository->findBy(['code' => ['12345', '500']])->willReturn([$firstAssociatedProduct, $secondAssociatedProduct]);
+ $productRepository->findBy(['code' => ['10', '20']])->willReturn([$thirdAssociatedProduct, $fourthAssociatedProduct]);
+
+ $firstProductAssociation->clearAssociatedProducts()->shouldBeCalled();
+ $firstProductAssociation->addAssociatedProduct($firstAssociatedProduct)->shouldBeCalled();
+ $firstProductAssociation->addAssociatedProduct($secondAssociatedProduct)->shouldBeCalled();
+
+ $secondProductAssociation->clearAssociatedProducts()->shouldBeCalled();
+ $secondProductAssociation->addAssociatedProduct($thirdAssociatedProduct)->shouldBeCalled();
+ $secondProductAssociation->addAssociatedProduct($fourthAssociatedProduct)->shouldBeCalled();
+
+ $entityManager->persist($firstProductAssociation)->shouldBeCalled();
+ $entityManager->persist($secondProductAssociation)->shouldBeCalled();
+ $entityManager->flush()->shouldBeCalledTimes(3);
+
+ $processor(new SynchronizationEnded(Uuid::v4(), new \DateTimeImmutable(), 10, ['10137', '12345']));
+ }
}
diff --git a/tests/Unit/Provider/SynchronizableProductsNumberProviderTest.php b/tests/Unit/Provider/SynchronizableProductsNumberProviderTest.php
new file mode 100644
index 0000000..3dfc1f7
--- /dev/null
+++ b/tests/Unit/Provider/SynchronizableProductsNumberProviderTest.php
@@ -0,0 +1,67 @@
+setNumberOfSynchronisedProducts(5);
+ $product->addChannel($channel);
+
+ $provider = new SynchronizableProductsNumberProvider();
+ $this->assertSame(5, $provider->getNumberOfProductsToSynchronise($product));
+ }
+
+ public function testItProvidesTheHighestOfConfiguredNumbers(): void
+ {
+ $product = new Product();
+ $firstChannel = new class extends Channel implements BoughtTogetherConfigurableChannelInterface {
+ use BoughtTogetherConfigurableChannelTrait;
+ };
+ $firstChannel->setNumberOfSynchronisedProducts(5);
+ $product->addChannel($firstChannel);
+ $secondChannel = new class extends Channel implements BoughtTogetherConfigurableChannelInterface {
+ use BoughtTogetherConfigurableChannelTrait;
+ };
+ $secondChannel->setNumberOfSynchronisedProducts(10);
+ $product->addChannel($secondChannel);
+ $thirdChannel = new class extends Channel implements BoughtTogetherConfigurableChannelInterface {
+ use BoughtTogetherConfigurableChannelTrait;
+ };
+ $thirdChannel->setNumberOfSynchronisedProducts(7);
+ $product->addChannel($thirdChannel);
+
+ $provider = new SynchronizableProductsNumberProvider();
+ $this->assertSame(10, $provider->getNumberOfProductsToSynchronise($product));
+ }
+
+ public function testItProvidesTheDefaultNumberOfProductsIfNoChannelConfigured(): void
+ {
+ $product = new Product();
+
+ $provider = new SynchronizableProductsNumberProvider();
+ $this->assertSame(10, $provider->getNumberOfProductsToSynchronise($product));
+ }
+
+ public function testItProvidesTheCustomDefaultNumberOfProductsIfNoChannelConfigured(): void
+ {
+ $product = new Product();
+
+ $provider = new SynchronizableProductsNumberProvider(15);
+ $this->assertSame(15, $provider->getNumberOfProductsToSynchronise($product));
+ }
+}
diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml
index 843d85f..df8ed9e 100644
--- a/translations/messages.en.yaml
+++ b/translations/messages.en.yaml
@@ -1,4 +1,7 @@
commerce_weavers_sylius_also_bought:
+ form:
+ channel:
+ number_of_synchronised_products: 'Number of synchronised products'
missing_configuration_error_message:
title: 'SyliusAlsoBoughtPlugin: manual action required'
error_explanation: 'SyliusAlsoBoughtPlugin is installed, but the necessary product association type with code "bought_together" does not exist.'