Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PromotionCouponEligbilityValidator refactoring
Browse files Browse the repository at this point in the history
Zales0123 authored and Tomanhez committed Jun 24, 2021
1 parent 9cd2810 commit a2b7694
Showing 6 changed files with 228 additions and 127 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?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\ApiBundle\Checker;

use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PromotionCouponInterface;
use Sylius\Component\Core\Model\PromotionInterface;
use Sylius\Component\Promotion\Checker\Eligibility\PromotionCouponEligibilityCheckerInterface;
use Sylius\Component\Promotion\Checker\Eligibility\PromotionEligibilityCheckerInterface;

final class AppliedCouponEligibilityChecker implements AppliedCouponEligibilityCheckerInterface
{
/** @var PromotionEligibilityCheckerInterface */
private $promotionChecker;

/** @var PromotionCouponEligibilityCheckerInterface */
private $promotionCouponChecker;

public function __construct(
PromotionEligibilityCheckerInterface $promotionChecker,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker
) {
$this->promotionChecker = $promotionChecker;
$this->promotionCouponChecker = $promotionCouponChecker;
}

public function isEligible(PromotionCouponInterface $promotionCoupon, OrderInterface $cart): bool
{
/** @var PromotionInterface $promotion */
$promotion = $promotionCoupon->getPromotion();

if (!$promotion->getChannels()->contains($cart->getChannel())) {
return false;
}

if (!$this->promotionCouponChecker->isEligible($cart, $promotionCoupon)) {
return false;
}

if (!$this->promotionChecker->isEligible($cart, $promotionCoupon->getPromotion())) {
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Sylius\Bundle\ApiBundle\Checker;

use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PromotionCouponInterface;

interface AppliedCouponEligibilityCheckerInterface
{
public function isEligible(PromotionCouponInterface $promotionCoupon, OrderInterface $cart): bool;
}
Original file line number Diff line number Diff line change
@@ -93,9 +93,13 @@
<service id="sylius.api.validator.promotion_coupon_eligibility" class="Sylius\Bundle\ApiBundle\Validator\Constraints\PromotionCouponEligibilityValidator">
<argument type="service" id="sylius.repository.promotion_coupon" />
<argument type="service" id="sylius.repository.order" />
<argument type="service" id="sylius.api.checker.applied_coupon_eligibility_checker" />
<tag name="validator.constraint_validator" alias="sylius_api_promotion_coupon_eligibility" />
</service>

<service id="sylius.api.checker.applied_coupon_eligibility_checker" class="Sylius\Bundle\ApiBundle\Checker\AppliedCouponEligibilityChecker">
<argument type="service" id="sylius.promotion_eligibility_checker" />
<argument type="service" id="sylius.promotion_coupon_eligibility_checker" />
<tag name="validator.constraint_validator" alias="sylius_api_promotion_coupon_eligibility" />
</service>

<service id="sylius.api.validator.shipment_already_shipped" class="Sylius\Bundle\ApiBundle\Validator\Constraints\ShipmentAlreadyShippedValidator">
Original file line number Diff line number Diff line change
@@ -13,13 +13,11 @@

namespace Sylius\Bundle\ApiBundle\Validator\Constraints;

use Sylius\Bundle\ApiBundle\Checker\AppliedCouponEligibilityCheckerInterface;
use Sylius\Bundle\ApiBundle\Command\Cart\ApplyCouponToCart;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PromotionInterface;
use Sylius\Component\Core\Model\PromotionCouponInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Promotion\Checker\Eligibility\PromotionCouponEligibilityCheckerInterface;
use Sylius\Component\Promotion\Checker\Eligibility\PromotionEligibilityCheckerInterface;
use Sylius\Component\Promotion\Model\PromotionCouponInterface;
use Sylius\Component\Promotion\Repository\PromotionCouponRepositoryInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
@@ -34,22 +32,17 @@ final class PromotionCouponEligibilityValidator extends ConstraintValidator
/** @var OrderRepositoryInterface */
private $orderRepository;

/** @var PromotionEligibilityCheckerInterface */
private $promotionChecker;

/** @var PromotionCouponEligibilityCheckerInterface */
private $promotionCouponChecker;
/** @var AppliedCouponEligibilityCheckerInterface */
private $appliedCouponEligibilityChecker;

public function __construct(
PromotionCouponRepositoryInterface $promotionCouponRepository,
OrderRepositoryInterface $orderRepository,
PromotionEligibilityCheckerInterface $promotionChecker,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker
AppliedCouponEligibilityCheckerInterface $appliedCouponEligibilityChecker
) {
$this->promotionCouponRepository = $promotionCouponRepository;
$this->orderRepository = $orderRepository;
$this->promotionChecker = $promotionChecker;
$this->promotionCouponChecker = $promotionCouponChecker;
$this->appliedCouponEligibilityChecker = $appliedCouponEligibilityChecker;
}

public function validate($value, Constraint $constraint): void
@@ -62,26 +55,20 @@ public function validate($value, Constraint $constraint): void

/** @var PromotionCouponInterface|null $promotionCoupon */
$promotionCoupon = $this->promotionCouponRepository->findOneBy(['code' => $value->couponCode]);
/** @var PromotionInterface $promotion */
$promotion = $promotionCoupon->getPromotion();

/** @var OrderInterface $cart */
$cart = $this->orderRepository->findCartByTokenValue($value->getOrderTokenValue());

$cart->setPromotionCoupon($promotionCoupon);

if (
$promotionCoupon === null ||
!$this->promotionCouponChecker->isEligible($cart, $promotionCoupon) ||
!$this->promotionChecker->isEligible($cart, $promotionCoupon->getPromotion()) ||
!$promotion->getChannels()->contains($cart->getChannel())
!$this->appliedCouponEligibilityChecker->isEligible($promotionCoupon, $cart)
) {
$this->addViolation('sylius.promotion_coupon.is_invalid', 'couponCode');
$this->context
->buildViolation('sylius.promotion_coupon.is_invalid')
->atPath('couponCode')
->addViolation()
;
}
}

private function addViolation(string $message, string $path): void
{
$this->context->buildViolation($message)->atPath($path)->addViolation();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?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 spec\Sylius\Bundle\ApiBundle\Checker;

use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Bundle\ApiBundle\Checker\AppliedCouponEligibilityCheckerInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PromotionCouponInterface;
use Sylius\Component\Core\Model\PromotionInterface;
use Sylius\Component\Promotion\Checker\Eligibility\PromotionCouponEligibilityCheckerInterface;
use Sylius\Component\Promotion\Checker\Eligibility\PromotionEligibilityCheckerInterface;

final class AppliedCouponEligibilityCheckerSpec extends ObjectBehavior
{
function let(
PromotionEligibilityCheckerInterface $promotionChecker,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker
): void {
$this->beConstructedWith($promotionChecker, $promotionCouponChecker);
}

function it_implements_promotion_coupon_eligibility_checker_interface(): void
{
$this->shouldImplement(AppliedCouponEligibilityCheckerInterface::class);
}

function it_returns_false_if_cart_channel_is_not_one_of_promotion_channels(
PromotionEligibilityCheckerInterface $promotionChecker,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker,
PromotionCouponInterface $promotionCoupon,
PromotionInterface $promotion,
OrderInterface $cart,
ChannelInterface $firstChannel,
ChannelInterface $secondChannel,
ChannelInterface $thirdChannel
): void {
$promotionCoupon->getPromotion()->willReturn($promotion);

$promotion->getChannels()->willReturn(new ArrayCollection([
$secondChannel->getWrappedObject(),
$thirdChannel->getWrappedObject()
]));
$cart->getChannel()->willReturn($firstChannel);

$promotionChecker->isEligible(Argument::any())->shouldNotBeCalled();
$promotionCouponChecker->isEligible(Argument::any())->shouldNotBeCalled();

$this->isEligible($promotionCoupon, $cart)->shouldReturn(false);
}

function it_returns_false_if_coupon_is_not_eligible(
PromotionEligibilityCheckerInterface $promotionChecker,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker,
PromotionCouponInterface $promotionCoupon,
PromotionInterface $promotion,
OrderInterface $cart,
ChannelInterface $firstChannel,
ChannelInterface $secondChannel
): void {
$promotionCoupon->getPromotion()->willReturn($promotion);

$promotion->getChannels()->willReturn(new ArrayCollection([
$firstChannel->getWrappedObject(),
$secondChannel->getWrappedObject()
]));
$cart->getChannel()->willReturn($firstChannel);

$promotionCouponChecker->isEligible($cart, $promotionCoupon)->willReturn(false);
$promotionChecker->isEligible(Argument::any())->shouldNotBeCalled();

$this->isEligible($promotionCoupon, $cart)->shouldReturn(false);
}

function it_returns_false_if_promotion_is_not_eligible(
PromotionEligibilityCheckerInterface $promotionChecker,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker,
PromotionCouponInterface $promotionCoupon,
PromotionInterface $promotion,
OrderInterface $cart,
ChannelInterface $firstChannel,
ChannelInterface $secondChannel
): void {
$promotionCoupon->getPromotion()->willReturn($promotion);

$promotion->getChannels()->willReturn(new ArrayCollection([
$firstChannel->getWrappedObject(),
$secondChannel->getWrappedObject()
]));
$cart->getChannel()->willReturn($firstChannel);

$promotionCouponChecker->isEligible($cart, $promotionCoupon)->willReturn(true);
$promotionChecker->isEligible($cart, $promotion)->willReturn(false);

$this->isEligible($promotionCoupon, $cart)->shouldReturn(false);
}

function it_returns_true_if_promotion_and_coupon_are_eligible(
PromotionEligibilityCheckerInterface $promotionChecker,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker,
PromotionCouponInterface $promotionCoupon,
PromotionInterface $promotion,
OrderInterface $cart,
ChannelInterface $firstChannel,
ChannelInterface $secondChannel
): void {
$promotionCoupon->getPromotion()->willReturn($promotion);

$promotion->getChannels()->willReturn(new ArrayCollection([
$firstChannel->getWrappedObject(),
$secondChannel->getWrappedObject()
]));
$cart->getChannel()->willReturn($firstChannel);

$promotionCouponChecker->isEligible($cart, $promotionCoupon)->willReturn(true);
$promotionChecker->isEligible($cart, $promotion)->willReturn(true);

$this->isEligible($promotionCoupon, $cart)->shouldReturn(true);
}
}
Original file line number Diff line number Diff line change
@@ -13,17 +13,13 @@

namespace spec\Sylius\Bundle\ApiBundle\Validator\Constraints;

use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Checker\AppliedCouponEligibilityCheckerInterface;
use Sylius\Bundle\ApiBundle\Command\Cart\ApplyCouponToCart;
use Sylius\Bundle\ApiBundle\Validator\Constraints\PromotionCouponEligibility;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PromotionCouponInterface;
use Sylius\Component\Core\Model\PromotionInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Promotion\Checker\Eligibility\PromotionCouponEligibilityCheckerInterface;
use Sylius\Component\Promotion\Checker\Eligibility\PromotionEligibilityCheckerInterface;
use Sylius\Component\Promotion\Repository\PromotionCouponRepositoryInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorInterface;
@@ -35,14 +31,12 @@ final class PromotionCouponEligibilityValidatorSpec extends ObjectBehavior
function let(
PromotionCouponRepositoryInterface $promotionCouponRepository,
OrderRepositoryInterface $orderRepository,
PromotionEligibilityCheckerInterface $promotionChecker,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker
AppliedCouponEligibilityCheckerInterface $appliedCouponEligibilityChecker
): void {
$this->beConstructedWith(
$promotionCouponRepository,
$orderRepository,
$promotionChecker,
$promotionCouponChecker
$appliedCouponEligibilityChecker
);
}

@@ -61,15 +55,11 @@ function it_throws_an_exception_if_constraint_is_not_of_expected_type(): void

function it_does_not_add_violation_if_promotion_coupon_is_eligible(
PromotionCouponRepositoryInterface $promotionCouponRepository,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker,
PromotionEligibilityCheckerInterface $promotionChecker,
AppliedCouponEligibilityCheckerInterface $appliedCouponEligibilityChecker,
PromotionCouponInterface $promotionCoupon,
PromotionInterface $promotion,
OrderRepositoryInterface $orderRepository,
OrderInterface $cart,
ExecutionContextInterface $executionContext,
ChannelInterface $firstChannel,
ChannelInterface $secondChannel
ExecutionContextInterface $executionContext
): void {
$this->initialize($executionContext);
$constraint = new PromotionCouponEligibility();
@@ -78,18 +68,11 @@ function it_does_not_add_violation_if_promotion_coupon_is_eligible(
$value->setOrderTokenValue('token');

$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon);

$orderRepository->findCartByTokenValue('token')->willReturn($cart);
$cart->getChannel()->willReturn($firstChannel);

$cart->setPromotionCoupon($promotionCoupon)->shouldBeCalled();

$promotionCouponChecker->isEligible($cart, $promotionCoupon)->willReturn(true);

$promotionCoupon->getPromotion()->willReturn($promotion);
$promotion->getChannels()->willReturn(new ArrayCollection([$firstChannel->getWrappedObject(), $secondChannel->getWrappedObject()]));

$promotionChecker->isEligible($cart, $promotion)->willReturn(true);
$appliedCouponEligibilityChecker->isEligible($promotionCoupon, $cart)->willReturn(true);

$executionContext->buildViolation('sylius.promotion_coupon.is_invalid')->shouldNotBeCalled();

@@ -98,80 +81,8 @@ function it_does_not_add_violation_if_promotion_coupon_is_eligible(

function it_adds_violation_if_promotion_coupon_is_not_eligible(
PromotionCouponRepositoryInterface $promotionCouponRepository,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker,
PromotionCouponInterface $promotionCoupon,
OrderRepositoryInterface $orderRepository,
OrderInterface $cart,
ExecutionContextInterface $executionContext,
ConstraintViolationBuilderInterface $constraintViolationBuilder
): void {
$this->initialize($executionContext);
$constraint = new PromotionCouponEligibility();

$value = new ApplyCouponToCart('couponCode');
$value->setOrderTokenValue('token');

$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon);

$orderRepository->findCartByTokenValue('token')->willReturn($cart);

$cart->setPromotionCoupon($promotionCoupon)->shouldBeCalled();

$promotionCouponChecker->isEligible($cart, $promotionCoupon)->willReturn(false);

$executionContext->buildViolation('sylius.promotion_coupon.is_invalid')->willReturn($constraintViolationBuilder);
$constraintViolationBuilder->atPath('couponCode')->willReturn($constraintViolationBuilder);
$constraintViolationBuilder->addViolation()->shouldBeCalled();

$this->validate($value, $constraint);
}

function it_adds_violation_if_promotion_is_not_available_in_cart_channel(
PromotionCouponRepositoryInterface $promotionCouponRepository,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker,
PromotionEligibilityCheckerInterface $promotionChecker,
PromotionCouponInterface $promotionCoupon,
PromotionInterface $promotion,
OrderRepositoryInterface $orderRepository,
OrderInterface $cart,
ExecutionContextInterface $executionContext,
ConstraintViolationBuilderInterface $constraintViolationBuilder,
ChannelInterface $firstChannel,
ChannelInterface $secondChannel,
ChannelInterface $thirdChannel
): void {
$this->initialize($executionContext);
$constraint = new PromotionCouponEligibility();

$value = new ApplyCouponToCart('couponCode');
$value->setOrderTokenValue('token');

$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon);
$promotionCoupon->getPromotion()->willReturn($promotion);
$promotion->getChannels()->willReturn(new ArrayCollection([$firstChannel->getWrappedObject(), $secondChannel->getWrappedObject()]));

$orderRepository->findCartByTokenValue('token')->willReturn($cart);
$cart->getChannel()->willReturn($thirdChannel);

$cart->setPromotionCoupon($promotionCoupon)->shouldBeCalled();

$promotionCouponChecker->isEligible($cart, $promotionCoupon)->willReturn(true);

$promotionChecker->isEligible($cart, $promotion)->willReturn(false);

$executionContext->buildViolation('sylius.promotion_coupon.is_invalid')->willReturn($constraintViolationBuilder);
$constraintViolationBuilder->atPath('couponCode')->willReturn($constraintViolationBuilder);
$constraintViolationBuilder->addViolation()->shouldBeCalled();

$this->validate($value, $constraint);
}

function it_adds_violation_if_promotion_is_not_eligible(
PromotionCouponRepositoryInterface $promotionCouponRepository,
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker,
PromotionEligibilityCheckerInterface $promotionChecker,
AppliedCouponEligibilityCheckerInterface $appliedCouponEligibilityChecker,
PromotionCouponInterface $promotionCoupon,
PromotionInterface $promotion,
OrderRepositoryInterface $orderRepository,
OrderInterface $cart,
ExecutionContextInterface $executionContext,
@@ -184,15 +95,11 @@ function it_adds_violation_if_promotion_is_not_eligible(
$value->setOrderTokenValue('token');

$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon);

$orderRepository->findCartByTokenValue('token')->willReturn($cart);

$cart->setPromotionCoupon($promotionCoupon)->shouldBeCalled();

$promotionCouponChecker->isEligible($cart, $promotionCoupon)->willReturn(true);

$promotionCoupon->getPromotion()->willReturn($promotion);
$promotionChecker->isEligible($cart, $promotion)->willReturn(false);
$appliedCouponEligibilityChecker->isEligible($promotionCoupon, $cart)->willReturn(false);

$executionContext->buildViolation('sylius.promotion_coupon.is_invalid')->willReturn($constraintViolationBuilder);
$constraintViolationBuilder->atPath('couponCode')->willReturn($constraintViolationBuilder);

0 comments on commit a2b7694

Please sign in to comment.