mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 17:10:53 +00:00
Include children taxons while executing InForTaxonsScopeVariantChecker
This commit is contained in:
parent
f9633e9745
commit
8e38c77061
8 changed files with 186 additions and 4 deletions
|
|
@ -7,12 +7,13 @@ Feature: Applying catalog promotions for taxon
|
|||
Background:
|
||||
Given the store operates on a single channel in "United States"
|
||||
And the store classifies its products as "Clothes" and "Dishes"
|
||||
And the "Clothes" taxon has child taxon "Jeans"
|
||||
And the "Jeans" taxon has child taxon "Programmer's jeans"
|
||||
And the store has a "T-Shirt" configurable product
|
||||
And this product belongs to "Clothes"
|
||||
And this product has "PHP T-Shirt" variant priced at "$20.00"
|
||||
And the store has a "Pants" configurable product
|
||||
And this product belongs to "Clothes"
|
||||
And this product has "Aladdin Pants" variant priced at "$100.00"
|
||||
And the store has a product "Blue Jeans" priced at "$100.00"
|
||||
And this product belongs to "Jeans"
|
||||
And the store has a "Mug" configurable product
|
||||
And this product belongs to "Dishes"
|
||||
And this product has "PHP Mug" variant priced at "$5.00"
|
||||
|
|
@ -24,6 +25,12 @@ Feature: Applying catalog promotions for taxon
|
|||
Then I should see the product price "$14.00"
|
||||
And I should see the product original price "$20.00"
|
||||
|
||||
@api @ui
|
||||
Scenario: Applying simple catalog promotions on a product belongs to child taxon
|
||||
When I view product "Blue Jeans"
|
||||
Then I should see the product price "$70.00"
|
||||
And I should see the product original price "$100.00"
|
||||
|
||||
@api @ui
|
||||
Scenario: Applying multiple catalog promotions
|
||||
Given there is a catalog promotion "Summer sale" that reduces price by "10%" and applies on "Clothes" taxon
|
||||
|
|
|
|||
|
|
@ -13,16 +13,24 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Bundle\CoreBundle\CatalogPromotion\Checker;
|
||||
|
||||
use Sylius\Bundle\TaxonomyBundle\Repository\TaxonTreeRepositoryInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Sylius\Component\Core\Model\ProductVariantInterface;
|
||||
use Sylius\Component\Core\Model\TaxonInterface;
|
||||
use Sylius\Component\Promotion\Model\CatalogPromotionScopeInterface;
|
||||
use Sylius\Component\Taxonomy\Repository\TaxonRepositoryInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class InForTaxonsScopeVariantChecker implements VariantInScopeCheckerInterface
|
||||
{
|
||||
public const TYPE = 'for_taxons';
|
||||
|
||||
public function __construct (
|
||||
private TaxonRepositoryInterface $taxonRepository,
|
||||
private TaxonTreeRepositoryInterface $taxonTreeRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function inScope(CatalogPromotionScopeInterface $scope, ProductVariantInterface $productVariant): bool
|
||||
{
|
||||
$configuration = $scope->getConfiguration();
|
||||
|
|
@ -31,8 +39,29 @@ final class InForTaxonsScopeVariantChecker implements VariantInScopeCheckerInter
|
|||
/** @var ProductInterface $product */
|
||||
$product = $productVariant->getProduct();
|
||||
|
||||
$promotionTaxons = $configuration['taxons'];
|
||||
foreach ($scope->getConfiguration()['taxons'] as $taxonCode) {
|
||||
/** @var TaxonInterface $taxon */
|
||||
$taxon = $this->taxonRepository->findOneBy(['code' => $taxonCode]);
|
||||
Assert::notNull($taxon, sprintf('Taxon with code "%s" does not exist', $taxonCode));
|
||||
|
||||
$promotionTaxons = array_merge($promotionTaxons, $this->getTaxonChildrenCodes($taxon));
|
||||
}
|
||||
|
||||
return $product->getTaxons()->exists(
|
||||
fn ($key, TaxonInterface $taxon): bool => \in_array($taxon->getCode(), $scope->getConfiguration()['taxons'], true),
|
||||
fn ($key, TaxonInterface $taxon): bool => \in_array($taxon->getCode(), $promotionTaxons, true),
|
||||
);
|
||||
}
|
||||
|
||||
/** @return array<string> */
|
||||
private function getTaxonChildrenCodes(TaxonInterface $taxon): array
|
||||
{
|
||||
$childrenCodes = [];
|
||||
|
||||
foreach ($this->taxonTreeRepository->children($taxon) as $taxonChild) {
|
||||
$childrenCodes[] = $taxonChild->getCode();
|
||||
}
|
||||
|
||||
return $childrenCodes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\CoreBundle\CatalogPromotion\Checker\InForTaxonsScopeVariantChecker">
|
||||
<argument type="service" id="sylius.repository.taxon" />
|
||||
<argument type="service" id="sylius.repository.tree.taxon" />
|
||||
<tag name="sylius.catalog_promotion.variant_checker" type="for_taxons" />
|
||||
</service>
|
||||
|
||||
|
|
|
|||
|
|
@ -16,26 +16,46 @@ namespace spec\Sylius\Bundle\CoreBundle\CatalogPromotion\Checker;
|
|||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\CoreBundle\CatalogPromotion\Checker\VariantInScopeCheckerInterface;
|
||||
use Sylius\Bundle\TaxonomyBundle\Repository\TaxonTreeRepositoryInterface;
|
||||
use Sylius\Component\Core\Model\CatalogPromotionScopeInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Sylius\Component\Core\Model\ProductVariantInterface;
|
||||
use Sylius\Component\Core\Model\TaxonInterface;
|
||||
use Sylius\Component\Taxonomy\Repository\TaxonRepositoryInterface;
|
||||
|
||||
final class InForTaxonsScopeVariantCheckerSpec extends ObjectBehavior
|
||||
{
|
||||
function let(TaxonRepositoryInterface $taxonRepository, TaxonTreeRepositoryInterface $taxonTreeRepository): void
|
||||
{
|
||||
$this->beConstructedWith($taxonRepository, $taxonTreeRepository);
|
||||
}
|
||||
|
||||
function it_implements_catalog_promotion_price_calculator_interface(): void
|
||||
{
|
||||
$this->shouldImplement(VariantInScopeCheckerInterface::class);
|
||||
}
|
||||
|
||||
public function it_returns_true_if_variant_taxon_is_in_scope_configuration(
|
||||
TaxonRepositoryInterface $taxonRepository,
|
||||
TaxonTreeRepositoryInterface $taxonTreeRepository,
|
||||
CatalogPromotionScopeInterface $scope,
|
||||
ProductVariantInterface $variant,
|
||||
ProductInterface $product,
|
||||
TaxonInterface $firstTaxon,
|
||||
TaxonInterface $secondTaxon,
|
||||
TaxonInterface $thirdTaxon,
|
||||
TaxonInterface $fourthTaxon,
|
||||
): void {
|
||||
$taxonRepository->findOneBy(['code' => 'FIRST_TAXON'])->willReturn($firstTaxon);
|
||||
$taxonRepository->findOneBy(['code' => 'SECOND_TAXON'])->willReturn($secondTaxon);
|
||||
$taxonRepository->findOneBy(['code' => 'THIRD_TAXON'])->willReturn($thirdTaxon);
|
||||
$taxonRepository->findOneBy(['code' => 'FOURTH_TAXON'])->willReturn($fourthTaxon);
|
||||
|
||||
$taxonTreeRepository->children($firstTaxon)->willReturn([]);
|
||||
$taxonTreeRepository->children($secondTaxon)->willReturn([]);
|
||||
$taxonTreeRepository->children($thirdTaxon)->willReturn([]);
|
||||
$taxonTreeRepository->children($fourthTaxon)->willReturn([]);
|
||||
|
||||
$scope->getConfiguration()->willReturn(['taxons' => ['FIRST_TAXON', 'SECOND_TAXON']]);
|
||||
|
||||
$variant->getProduct()->willReturn($product);
|
||||
|
|
@ -44,11 +64,38 @@ final class InForTaxonsScopeVariantCheckerSpec extends ObjectBehavior
|
|||
$firstTaxon->getCode()->willReturn('FIRST_TAXON');
|
||||
$secondTaxon->getCode()->willReturn('SECOND_TAXON');
|
||||
$thirdTaxon->getCode()->willReturn('THIRD_TAXON');
|
||||
$fourthTaxon->getCode()->willReturn('FOURTH_TAXON');
|
||||
|
||||
$this->inScope($scope, $variant)->shouldReturn(true);
|
||||
}
|
||||
|
||||
public function it_returns_true_if_variant_taxon_is_a_child_of_taxon_in_the_scope_configuration(
|
||||
TaxonRepositoryInterface $taxonRepository,
|
||||
TaxonTreeRepositoryInterface $taxonTreeRepository,
|
||||
CatalogPromotionScopeInterface $scope,
|
||||
ProductVariantInterface $variant,
|
||||
ProductInterface $product,
|
||||
TaxonInterface $firstTaxon,
|
||||
TaxonInterface $secondTaxon,
|
||||
): void {
|
||||
$taxonRepository->findOneBy(['code' => 'FIRST_TAXON'])->willReturn($firstTaxon);
|
||||
|
||||
$taxonTreeRepository->children($firstTaxon)->willReturn([$secondTaxon]);
|
||||
|
||||
$scope->getConfiguration()->willReturn(['taxons' => ['FIRST_TAXON']]);
|
||||
|
||||
$variant->getProduct()->willReturn($product);
|
||||
$product->getTaxons()->willReturn(new ArrayCollection([$secondTaxon->getWrappedObject()]));
|
||||
|
||||
$firstTaxon->getCode()->willReturn('FIRST_TAXON');
|
||||
$secondTaxon->getCode()->willReturn('SECOND_TAXON');
|
||||
|
||||
$this->inScope($scope, $variant)->shouldReturn(true);
|
||||
}
|
||||
|
||||
public function it_returns_false_if_variant_taxon_is_not_in_scope_configuration(
|
||||
TaxonRepositoryInterface $taxonRepository,
|
||||
TaxonTreeRepositoryInterface $taxonTreeRepository,
|
||||
CatalogPromotionScopeInterface $scope,
|
||||
ProductVariantInterface $variant,
|
||||
ProductInterface $product,
|
||||
|
|
@ -56,6 +103,12 @@ final class InForTaxonsScopeVariantCheckerSpec extends ObjectBehavior
|
|||
TaxonInterface $secondTaxon,
|
||||
TaxonInterface $thirdTaxon,
|
||||
): void {
|
||||
$taxonRepository->findOneBy(['code' => 'FIRST_TAXON'])->willReturn($firstTaxon);
|
||||
$taxonRepository->findOneBy(['code' => 'SECOND_TAXON'])->willReturn($secondTaxon);
|
||||
|
||||
$taxonTreeRepository->children($firstTaxon)->willReturn([]);
|
||||
$taxonTreeRepository->children($secondTaxon)->willReturn([]);
|
||||
|
||||
$scope->getConfiguration()->willReturn(['taxons' => ['FIRST_TAXON', 'SECOND_TAXON']]);
|
||||
|
||||
$variant->getProduct()->willReturn($product);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\TaxonomyBundle\Repository;
|
||||
|
||||
use Gedmo\Tree\Entity\Repository\NestedTreeRepository;
|
||||
|
||||
class TaxonTreeRepository implements TaxonTreeRepositoryInterface
|
||||
{
|
||||
public function __construct (
|
||||
private NestedTreeRepository $nestedTreeRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function children(?object $node = null, bool $direct = false, array|string|null $sortByField = null, array|string $direction = 'ASC', bool $includeNode = false,): array|null
|
||||
{
|
||||
return $this->nestedTreeRepository->children($node, $direct, $sortByField, $direction, $includeNode);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\TaxonomyBundle\Repository;
|
||||
|
||||
interface TaxonTreeRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* @param string|string[]|null $sortByField
|
||||
* @param string|string[] $direction
|
||||
*
|
||||
* @return array<object>|null
|
||||
*/
|
||||
public function children(
|
||||
?object $node = null,
|
||||
bool $direct = false,
|
||||
string|array|null $sortByField = null,
|
||||
string|array $direction = 'ASC',
|
||||
bool $includeNode = false,
|
||||
): array|null;
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<imports>
|
||||
<import resource="services/form.xml" />
|
||||
<import resource="services/tree_repository.xml" />
|
||||
</imports>
|
||||
|
||||
<services>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
|
||||
This file is part of the Sylius package.
|
||||
|
||||
(c) Sylius Sp. z o.o.
|
||||
|
||||
For the full copyright and license information, please view the LICENSE
|
||||
file that was distributed with this source code.
|
||||
|
||||
-->
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<service id="Sylius\Bundle\TaxonomyBundle\Repository\TaxonTreeRepositoryInterface" class="Sylius\Bundle\TaxonomyBundle\Repository\TaxonTreeRepository" decorates="sylius.repository.tree.taxon">
|
||||
<argument type="service" id="Sylius\Bundle\TaxonomyBundle\Repository\TaxonTreeRepositoryInterface.inner" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.repository.tree.taxon" class="Gedmo\Tree\Entity\Repository\NestedTreeRepository">
|
||||
<argument type="service" id="doctrine.orm.entity_manager" />
|
||||
<argument type="service">
|
||||
<service class="Doctrine\ORM\Mapping\ClassMetadata">
|
||||
<factory service="doctrine.orm.entity_manager" method="getClassMetadata" />
|
||||
<argument>%sylius.model.taxon.class%</argument>
|
||||
</service>
|
||||
</argument>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
Loading…
Add table
Reference in a new issue