mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
[ProductVariant][API] Add product validation
This commit is contained in:
parent
4875256995
commit
ad2c534af0
5 changed files with 206 additions and 7 deletions
|
|
@ -14,6 +14,8 @@ declare(strict_types=1);
|
|||
namespace Sylius\Bundle\CoreBundle\Validator\Constraints;
|
||||
|
||||
use Sylius\Component\Core\Model\ChannelPricingInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Sylius\Component\Core\Model\ProductVariantInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
|
@ -22,9 +24,17 @@ final class HasAllPricesDefinedValidator extends ConstraintValidator
|
|||
{
|
||||
public function validate($value, Constraint $constraint): void
|
||||
{
|
||||
Assert::isInstanceOf($value, ProductVariantInterface::class);
|
||||
Assert::isInstanceOf($constraint, HasAllPricesDefined::class);
|
||||
|
||||
$channels = $value->getProduct()->getChannels();
|
||||
/** @var ProductInterface|null $product */
|
||||
$product = $value->getProduct();
|
||||
|
||||
if ($product === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$channels = $product->getChannels();
|
||||
|
||||
foreach ($channels as $channel) {
|
||||
/** @var ChannelPricingInterface|null $channelPricing */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
<?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\CoreBundle\Validator\Constraints;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Bundle\CoreBundle\Validator\Constraints\HasAllPricesDefined;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\ChannelPricingInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Sylius\Component\Core\Model\ProductVariantInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidatorInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
|
||||
|
||||
final class HasAllPricesDefinedValidatorSpec extends ObjectBehavior
|
||||
{
|
||||
function let(ExecutionContextInterface $context): void
|
||||
{
|
||||
$this->initialize($context);
|
||||
}
|
||||
|
||||
function it_is_a_constraint_validator(): void
|
||||
{
|
||||
$this->shouldImplement(ConstraintValidatorInterface::class);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_if_value_is_not_a_product_variant(
|
||||
ExecutionContextInterface $context,
|
||||
): void {
|
||||
$context->buildViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this
|
||||
->shouldThrow(\InvalidArgumentException::class)
|
||||
->during('validate', [new \stdClass(), new HasAllPricesDefined()])
|
||||
;
|
||||
}
|
||||
|
||||
function it_throws_an_exception_if_constraint_is_not_a_has_all_prices_defined_constraint(
|
||||
ExecutionContextInterface $context,
|
||||
Constraint $constraint,
|
||||
): void {
|
||||
$context->buildViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this
|
||||
->shouldThrow(\InvalidArgumentException::class)
|
||||
->during('validate', [new \stdClass(), $constraint])
|
||||
;
|
||||
}
|
||||
|
||||
function it_does_nothing_if_product_variant_has_no_product(
|
||||
ExecutionContextInterface $context,
|
||||
ProductVariantInterface $productVariant,
|
||||
): void {
|
||||
$context->buildViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$productVariant->getProduct()->willReturn(null);
|
||||
|
||||
$this->validate($productVariant, new HasAllPricesDefined());
|
||||
}
|
||||
|
||||
function it_adds_a_violation_if_product_variant_does_not_have_any_channel_pricing(
|
||||
ExecutionContextInterface $context,
|
||||
ChannelInterface $firstChannel,
|
||||
ChannelInterface $secondChannel,
|
||||
ChannelPricingInterface $channelPricing,
|
||||
ConstraintViolationBuilderInterface $constraintViolationBuilder,
|
||||
ProductInterface $product,
|
||||
ProductVariantInterface $productVariant,
|
||||
): void {
|
||||
$productVariant->getProduct()->willReturn($product);
|
||||
$productVariant->getChannelPricingForChannel($firstChannel)->willReturn($channelPricing);
|
||||
$productVariant->getChannelPricingForChannel($secondChannel)->willReturn(null);
|
||||
|
||||
$channelPricing->getPrice()->willReturn(1000);
|
||||
|
||||
$product
|
||||
->getChannels()
|
||||
->willReturn(new ArrayCollection([$firstChannel->getWrappedObject(), $secondChannel->getWrappedObject()]))
|
||||
;
|
||||
|
||||
$constraintViolationBuilder->addViolation()->shouldBeCalled();
|
||||
$constraintViolationBuilder->atPath(Argument::any())->willReturn($constraintViolationBuilder);
|
||||
$constraintViolationBuilder->setParameter(Argument::cetera())->willReturn($constraintViolationBuilder);
|
||||
|
||||
$context->buildViolation((new HasAllPricesDefined())->message)->willReturn($constraintViolationBuilder);
|
||||
|
||||
$this->validate($productVariant, new HasAllPricesDefined());
|
||||
}
|
||||
|
||||
function it_adds_a_violation_if_product_variant_does_not_have_any_channel_pricing_price_defined(
|
||||
ExecutionContextInterface $context,
|
||||
ChannelInterface $firstChannel,
|
||||
ChannelInterface $secondChannel,
|
||||
ChannelPricingInterface $firstChannelPricing,
|
||||
ChannelPricingInterface $secondChannelPricing,
|
||||
ConstraintViolationBuilderInterface $constraintViolationBuilder,
|
||||
ProductInterface $product,
|
||||
ProductVariantInterface $productVariant,
|
||||
): void {
|
||||
$productVariant->getProduct()->willReturn($product);
|
||||
$productVariant->getChannelPricingForChannel($firstChannel)->willReturn($firstChannelPricing);
|
||||
$productVariant->getChannelPricingForChannel($secondChannel)->willReturn($secondChannelPricing);
|
||||
|
||||
$firstChannelPricing->getPrice()->willReturn(1000);
|
||||
$secondChannelPricing->getPrice()->willReturn(null);
|
||||
|
||||
$product
|
||||
->getChannels()
|
||||
->willReturn(new ArrayCollection([$firstChannel->getWrappedObject(), $secondChannel->getWrappedObject()]))
|
||||
;
|
||||
|
||||
$constraintViolationBuilder->addViolation()->shouldBeCalled();
|
||||
$constraintViolationBuilder->atPath(Argument::any())->willReturn($constraintViolationBuilder);
|
||||
$constraintViolationBuilder->setParameter(Argument::cetera())->willReturn($constraintViolationBuilder);
|
||||
|
||||
$context->buildViolation((new HasAllPricesDefined())->message)->willReturn($constraintViolationBuilder);
|
||||
|
||||
$this->validate($productVariant, new HasAllPricesDefined());
|
||||
}
|
||||
|
||||
function it_does_not_add_a_violation_if_product_variant_has_all_channel_pricing_prices_defined(
|
||||
ExecutionContextInterface $context,
|
||||
ChannelInterface $firstChannel,
|
||||
ChannelInterface $secondChannel,
|
||||
ChannelPricingInterface $firstChannelPricing,
|
||||
ChannelPricingInterface $secondChannelPricing,
|
||||
ProductInterface $product,
|
||||
ProductVariantInterface $productVariant,
|
||||
): void {
|
||||
$productVariant->getProduct()->willReturn($product);
|
||||
$productVariant->getChannelPricingForChannel($firstChannel)->willReturn($firstChannelPricing);
|
||||
$productVariant->getChannelPricingForChannel($secondChannel)->willReturn($secondChannelPricing);
|
||||
|
||||
$firstChannelPricing->getPrice()->willReturn(1000);
|
||||
$secondChannelPricing->getPrice()->willReturn(2000);
|
||||
|
||||
$product
|
||||
->getChannels()
|
||||
->willReturn(new ArrayCollection([$firstChannel->getWrappedObject(), $secondChannel->getWrappedObject()]))
|
||||
;
|
||||
|
||||
$context->buildViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this->validate($productVariant, new HasAllPricesDefined());
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,9 @@
|
|||
<option name="message">sylius.product_variant.code.unique</option>
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
<constraint name="Sylius\Bundle\ProductBundle\Validator\Constraint\ProductVariantCombination">
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
<property name="code">
|
||||
<constraint name="NotBlank">
|
||||
<option name="message">sylius.product_variant.code.not_blank</option>
|
||||
|
|
@ -29,8 +32,10 @@
|
|||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
</property>
|
||||
<constraint name="Sylius\Bundle\ProductBundle\Validator\Constraint\ProductVariantCombination">
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
<property name="product">
|
||||
<constraint name="NotBlank">
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
</property>
|
||||
</class>
|
||||
</constraint-mapping>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ final class ProductVariantCombinationValidator extends ConstraintValidator
|
|||
}
|
||||
|
||||
$product = $value->getProduct();
|
||||
if (!$product->hasVariants() || !$product->hasOptions()) {
|
||||
|
||||
if ($product === null || !$product->hasVariants() || !$product->hasOptions()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ final class ProductVariantCombinationValidatorSpec extends ObjectBehavior
|
|||
function let(ExecutionContextInterface $context, ProductVariantsParityCheckerInterface $variantsParityChecker): void
|
||||
{
|
||||
$this->beConstructedWith($variantsParityChecker);
|
||||
|
||||
$this->initialize($context);
|
||||
}
|
||||
|
||||
|
|
@ -35,6 +36,28 @@ final class ProductVariantCombinationValidatorSpec extends ObjectBehavior
|
|||
$this->shouldImplement(ConstraintValidator::class);
|
||||
}
|
||||
|
||||
function it_does_not_add_violation_if_product_is_null(
|
||||
ExecutionContextInterface $context,
|
||||
ProductInterface $product,
|
||||
ProductVariantInterface $variant,
|
||||
ProductVariantsParityCheckerInterface $variantsParityChecker,
|
||||
): void {
|
||||
$constraint = new ProductVariantCombination([
|
||||
'message' => 'Variant with given options already exists',
|
||||
]);
|
||||
|
||||
$variant->getProduct()->willReturn(null);
|
||||
|
||||
$product->hasVariants()->shouldNotBeCalled();
|
||||
$product->hasOptions()->shouldNotBeCalled();
|
||||
|
||||
$variantsParityChecker->checkParity($variant, $product)->shouldNotBeCalled();
|
||||
|
||||
$context->addViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this->validate($variant, $constraint);
|
||||
}
|
||||
|
||||
function it_does_not_add_violation_if_product_does_not_have_options(
|
||||
ExecutionContextInterface $context,
|
||||
ProductInterface $product,
|
||||
|
|
@ -50,7 +73,7 @@ final class ProductVariantCombinationValidatorSpec extends ObjectBehavior
|
|||
$product->hasVariants()->willReturn(true);
|
||||
$product->hasOptions()->willReturn(false);
|
||||
|
||||
$variantsParityChecker->checkParity($variant, $product)->willReturn(false);
|
||||
$variantsParityChecker->checkParity($variant, $product)->shouldNotBeCalled();
|
||||
|
||||
$context->addViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
|
|
@ -74,7 +97,7 @@ final class ProductVariantCombinationValidatorSpec extends ObjectBehavior
|
|||
|
||||
$context->addViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$variantsParityChecker->checkParity($variant, $product)->willReturn(false);
|
||||
$variantsParityChecker->checkParity($variant, $product)->shouldNotBeCalled();
|
||||
|
||||
$this->validate($variant, $constraint);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue