mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 17:10:53 +00:00
[Promotion] Switch catalog promotion action validation from custom solution to symfony
This commit is contained in:
parent
2c3f7ee0e8
commit
d0fe806281
23 changed files with 701 additions and 154 deletions
|
|
@ -4560,6 +4560,11 @@ parameters:
|
|||
count: 1
|
||||
path: src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php
|
||||
|
||||
-
|
||||
message: "#^Property Sylius\\\\Bundle\\\\PromotionBundle\\\\Validator\\\\CatalogPromotionActionValidator\\:\\:\\$actionTypes is never read, only written.$#"
|
||||
count: 1
|
||||
path: src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php
|
||||
|
||||
-
|
||||
message: "#^Method Sylius\\\\Bundle\\\\PromotionBundle\\\\Validator\\\\CatalogPromotionScope\\\\ScopeValidatorInterface\\:\\:validate\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ sylius_promotion:
|
|||
model: Sylius\Component\Core\Model\PromotionCoupon
|
||||
repository: Sylius\Bundle\PromotionBundle\Doctrine\ORM\PromotionCouponRepository
|
||||
|
||||
catalog_promotion_action:
|
||||
validation_groups:
|
||||
fixed_discount:
|
||||
- 'sylius'
|
||||
- 'sylius_catalog_promotion_action_fixed_discount'
|
||||
|
||||
catalog_promotion_scope:
|
||||
validation_groups:
|
||||
for_products:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
<?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.
|
||||
|
||||
-->
|
||||
|
||||
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/services/constraint-mapping-1.0.xsd">
|
||||
<class name="Sylius\Component\Promotion\Model\CatalogPromotionAction">
|
||||
<property name="configuration">
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\ChannelCodeCollection">
|
||||
<option name="groups">sylius_catalog_promotion_action_fixed_discount</option>
|
||||
<option name="constraints">
|
||||
<constraint name="Collection">
|
||||
<option name="groups">sylius_catalog_promotion_action_fixed_discount</option>
|
||||
<option name="fields">
|
||||
<value key="amount">
|
||||
<constraint name="NotBlank">
|
||||
<option name="groups">sylius_catalog_promotion_action_fixed_discount</option>
|
||||
</constraint>
|
||||
<constraint name="Type">
|
||||
<option name="type">numeric</option>
|
||||
<option name="groups">sylius_catalog_promotion_action_fixed_discount</option>
|
||||
</constraint>
|
||||
</value>
|
||||
</option>
|
||||
</constraint>
|
||||
</option>
|
||||
<option name="allowExtraFields">true</option>
|
||||
</constraint>
|
||||
</property>
|
||||
</class>
|
||||
</constraint-mapping>
|
||||
|
|
@ -76,6 +76,15 @@ final class Configuration implements ConfigurationInterface
|
|||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('catalog_promotion_action')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->arrayNode('validation_groups')
|
||||
->useAttributeAsKey('name')
|
||||
->variablePrototype()->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('catalog_promotion_scope')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
|
|
|
|||
|
|
@ -33,14 +33,20 @@ final class SyliusPromotionExtension extends AbstractResourceExtension
|
|||
$loader->load('services.xml');
|
||||
$loader->load(sprintf('services/integrations/%s.xml', $config['driver']));
|
||||
|
||||
$container->setParameter('sylius.promotion.promotion_action.validation_groups', $config['promotion_action']['validation_groups']);
|
||||
$container->setParameter('sylius.promotion.promotion_rule.validation_groups', $config['promotion_rule']['validation_groups']);
|
||||
$container->setParameter('sylius.promotion.catalog_promotion_scope.validation_groups', $config['catalog_promotion_scope']['validation_groups']);
|
||||
|
||||
$this->registerResources('sylius', $config['driver'], $config['resources'], $container);
|
||||
$this->registerValidationParameters($container, $config);
|
||||
$this->registerAutoconfiguration($container);
|
||||
}
|
||||
|
||||
/** @param array<string, array<string, array<array-key, string,>>> $configuration */
|
||||
private function registerValidationParameters(ContainerBuilder $container, array $configuration): void
|
||||
{
|
||||
$container->setParameter('sylius.promotion.promotion_action.validation_groups', $configuration['promotion_action']['validation_groups']);
|
||||
$container->setParameter('sylius.promotion.promotion_rule.validation_groups', $configuration['promotion_rule']['validation_groups']);
|
||||
$container->setParameter('sylius.promotion.catalog_promotion_action.validation_groups', $configuration['catalog_promotion_action']['validation_groups']);
|
||||
$container->setParameter('sylius.promotion.catalog_promotion_scope.validation_groups', $configuration['catalog_promotion_scope']['validation_groups']);
|
||||
}
|
||||
|
||||
private function registerAutoconfiguration(ContainerBuilder $container): void
|
||||
{
|
||||
$container->registerAttributeForAutoconfiguration(
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ use Sylius\Bundle\MoneyBundle\Form\Type\MoneyType;
|
|||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\Type;
|
||||
|
||||
final class FixedDiscountActionConfigurationType extends AbstractType
|
||||
{
|
||||
|
|
@ -26,9 +25,6 @@ final class FixedDiscountActionConfigurationType extends AbstractType
|
|||
$builder
|
||||
->add('amount', MoneyType::class, [
|
||||
'label' => 'sylius.ui.amount',
|
||||
'constraints' => [
|
||||
new Type(['type' => 'numeric', 'groups' => ['sylius']]),
|
||||
],
|
||||
'currency' => $options['currency'],
|
||||
])
|
||||
;
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ namespace Sylius\Bundle\PromotionBundle\Form\Type\CatalogPromotionAction;
|
|||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PercentType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Constraints\Range;
|
||||
|
||||
final class PercentageDiscountActionConfigurationType extends AbstractType
|
||||
{
|
||||
|
|
@ -28,18 +26,6 @@ final class PercentageDiscountActionConfigurationType extends AbstractType
|
|||
'label' => 'sylius.ui.amount',
|
||||
'html5' => true,
|
||||
'scale' => 2,
|
||||
'constraints' => [
|
||||
new NotBlank([
|
||||
'groups' => 'sylius',
|
||||
'message' => 'sylius.catalog_promotion_action.percentage_discount.not_number_or_empty',
|
||||
]),
|
||||
new Range([
|
||||
'min' => 0,
|
||||
'max' => 1,
|
||||
'notInRangeMessage' => 'sylius.catalog_promotion_action.percentage_discount.not_in_range',
|
||||
'groups' => ['sylius'],
|
||||
]),
|
||||
],
|
||||
])
|
||||
;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ sylius_promotion:
|
|||
unit_percentage_discount:
|
||||
- 'sylius'
|
||||
- 'sylius_promotion_action_unit_percentage_discount'
|
||||
|
||||
promotion_rule:
|
||||
validation_groups:
|
||||
cart_quantity:
|
||||
|
|
@ -52,3 +53,9 @@ sylius_promotion:
|
|||
item_total:
|
||||
- 'sylius'
|
||||
- 'sylius_promotion_rule_item_total'
|
||||
|
||||
catalog_promotion_action:
|
||||
validation_groups:
|
||||
percentage_discount:
|
||||
- 'sylius'
|
||||
- 'sylius_catalog_promotion_action_percentage_discount'
|
||||
|
|
|
|||
|
|
@ -35,6 +35,16 @@
|
|||
<tag name="validator.constraint_validator" alias="sylius_catalog_promotion_action" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\PromotionBundle\Validator\CatalogPromotionActionGroupValidator">
|
||||
<argument>%sylius.promotion.catalog_promotion_action.validation_groups%</argument>
|
||||
<tag name="validator.constraint_validator" alias="sylius_catalog_promotion_action_group" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\PromotionBundle\Validator\CatalogPromotionActionTypeValidator">
|
||||
<argument>%sylius.catalog_promotion.actions_types%</argument>
|
||||
<tag name="validator.constraint_validator" alias="sylius_catalog_promotion_action_type_validator" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\PromotionBundle\Validator\CatalogPromotionScopeValidator">
|
||||
<argument>%sylius.catalog_promotion.scopes_types%</argument>
|
||||
<argument type="tagged_iterator" tag="sylius.catalog_promotion.scope_validator" index-by="key" />
|
||||
|
|
|
|||
|
|
@ -13,8 +13,39 @@
|
|||
|
||||
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/services/constraint-mapping-1.0.xsd">
|
||||
<class name="Sylius\Component\Promotion\Model\CatalogPromotionAction">
|
||||
<constraint name="Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionAction">
|
||||
<constraint name="Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionActionType">
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
|
||||
<constraint name="Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionActionGroup">
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
|
||||
<property name="type">
|
||||
<constraint name="NotBlank">
|
||||
<option name="message">sylius.catalog_promotion_action.type.not_blank</option>
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
</property>
|
||||
<property name="configuration">
|
||||
<constraint name="Collection">
|
||||
<option name="groups">sylius_catalog_promotion_action_percentage_discount</option>
|
||||
<option name="fields">
|
||||
<value key="amount">
|
||||
<constraint name="NotBlank">
|
||||
<option name="message">sylius.catalog_promotion_action.percentage_discount.not_number_or_empty</option>
|
||||
<option name="groups">sylius_catalog_promotion_action_percentage_discount</option>
|
||||
</constraint>
|
||||
<constraint name="Range">
|
||||
<option name="min">0</option>
|
||||
<option name="max">1</option>
|
||||
<option name="notInRangeMessage">sylius.catalog_promotion_action.percentage_discount.not_in_range</option>
|
||||
<option name="groups">sylius_catalog_promotion_action_percentage_discount</option>
|
||||
</constraint>
|
||||
</value>
|
||||
</option>
|
||||
<option name="allowExtraFields">true</option>
|
||||
</constraint>
|
||||
</property>
|
||||
</class>
|
||||
</constraint-mapping>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ sylius:
|
|||
state:
|
||||
processing: The catalog promotion cannot be edited as it is currently being processed.
|
||||
catalog_promotion_action:
|
||||
type:
|
||||
not_blank: Please choose an action type.
|
||||
invalid: Catalog promotion action type is invalid. Available types are {{ available_action_types }}.
|
||||
invalid_type: Catalog promotion action type is invalid. Please choose a valid type.
|
||||
percentage_discount:
|
||||
not_in_range: The percentage discount amount must be between 0% and 100%.
|
||||
|
|
|
|||
|
|
@ -175,6 +175,35 @@ final class SyliusPromotionExtensionTest extends AbstractExtensionTestCase
|
|||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_loads_catalog_promotion_action_validation_groups_parameter_value_properly(): void
|
||||
{
|
||||
$this->load([
|
||||
'catalog_promotion_action' => [
|
||||
'validation_groups' => [
|
||||
'something' => ['sylius', 'something'],
|
||||
'test' => ['sylius', 'test'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertContainerBuilderHasParameter(
|
||||
'sylius.promotion.catalog_promotion_action.validation_groups',
|
||||
['something' => ['sylius', 'something'], 'test' => ['sylius', 'test']],
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_loads_empty_catalog_promotion_action_validation_groups_parameter_value(): void
|
||||
{
|
||||
$this->load();
|
||||
|
||||
$this->assertContainerBuilderHasParameter(
|
||||
'sylius.promotion.catalog_promotion_action.validation_groups',
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_loads_catalog_promotion_scope_validation_groups_parameter_value_properly(): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\PromotionBundle\Validator;
|
||||
|
||||
use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionAction;
|
||||
use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionActionGroup;
|
||||
use Sylius\Component\Promotion\Model\CatalogPromotionActionInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class CatalogPromotionActionGroupValidator extends ConstraintValidator
|
||||
{
|
||||
/** @param array<string, array<string, string>> $validationGroups */
|
||||
public function __construct(private array $validationGroups)
|
||||
{
|
||||
}
|
||||
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof CatalogPromotionActionGroup) {
|
||||
throw new UnexpectedTypeException($constraint, CatalogPromotionActionGroup::class);
|
||||
}
|
||||
|
||||
if (!$value instanceof CatalogPromotionActionInterface) {
|
||||
throw new UnexpectedTypeException($value, CatalogPromotionActionInterface::class);
|
||||
}
|
||||
|
||||
$type = $value->getType();
|
||||
if (null === $type || '' === $type) {
|
||||
return;
|
||||
}
|
||||
|
||||
$validator = $this->context->getValidator()->inContext($this->context);
|
||||
|
||||
$groups = $this->validationGroups[$type] ?? null;
|
||||
if (null !== $groups) {
|
||||
$validator->validate(value: $value, groups: $groups);
|
||||
|
||||
if ($this->context->getViolations()->count() > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$validator->validate(
|
||||
$value,
|
||||
new CatalogPromotionAction(groups: $constraint->groups),
|
||||
$groups ?? $constraint->groups,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\PromotionBundle\Validator;
|
||||
|
||||
use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionActionType;
|
||||
use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionScopeType;
|
||||
use Sylius\Component\Promotion\Model\CatalogPromotionActionInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class CatalogPromotionActionTypeValidator extends ConstraintValidator
|
||||
{
|
||||
/** @param array<array-key, string> $actionTypes */
|
||||
public function __construct(private array $actionTypes)
|
||||
{
|
||||
}
|
||||
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof CatalogPromotionActionType) {
|
||||
throw new UnexpectedTypeException($constraint, CatalogPromotionScopeType::class);
|
||||
}
|
||||
|
||||
if (!$value instanceof CatalogPromotionActionInterface) {
|
||||
throw new UnexpectedTypeException($value, CatalogPromotionActionInterface::class);
|
||||
}
|
||||
|
||||
$type = $value->getType();
|
||||
if (null === $type || '' === $type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!in_array($type, $this->actionTypes, true)) {
|
||||
$this->context->buildViolation($constraint->invalidType)
|
||||
->setParameter('{{ available_action_types }}', implode(', ', $this->actionTypes))
|
||||
->atPath('type')
|
||||
->addViolation()
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ namespace Sylius\Bundle\PromotionBundle\Validator;
|
|||
|
||||
use Sylius\Bundle\PromotionBundle\Validator\CatalogPromotionAction\ActionValidatorInterface;
|
||||
use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionAction;
|
||||
use Sylius\Component\Promotion\Model\CatalogPromotionActionInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
|
@ -34,13 +33,6 @@ final class CatalogPromotionActionValidator extends ConstraintValidator
|
|||
/** @var CatalogPromotionAction $constraint */
|
||||
Assert::isInstanceOf($constraint, CatalogPromotionAction::class);
|
||||
|
||||
/** @var CatalogPromotionActionInterface $value */
|
||||
if (!in_array($value->getType(), $this->actionTypes, true)) {
|
||||
$this->context->buildViolation($constraint->invalidType)->atPath('type')->addViolation();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$type = $value->getType();
|
||||
if (!array_key_exists($type, $this->actionValidators)) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\PromotionBundle\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
final class CatalogPromotionActionGroup extends Constraint
|
||||
{
|
||||
public function validatedBy(): string
|
||||
{
|
||||
return 'sylius_catalog_promotion_action_group';
|
||||
}
|
||||
|
||||
public function getTargets(): string
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\PromotionBundle\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
final class CatalogPromotionActionType extends Constraint
|
||||
{
|
||||
public string $invalidType = 'sylius.catalog_promotion_action.type.invalid';
|
||||
|
||||
public function validatedBy(): string
|
||||
{
|
||||
return 'sylius_catalog_promotion_action_type_validator';
|
||||
}
|
||||
|
||||
public function getTargets(): string
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\PromotionBundle\Validator;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionAction;
|
||||
use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionActionGroup;
|
||||
use Sylius\Component\Promotion\Model\CatalogPromotionActionInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Validator\ContextualValidatorInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
final class CatalogPromotionActionGroupValidatorSpec extends ObjectBehavior
|
||||
{
|
||||
private const VALIDATION_GROUPS = [
|
||||
'test' => [
|
||||
'test_group',
|
||||
],
|
||||
'another_test' => [
|
||||
'another_test_group',
|
||||
],
|
||||
];
|
||||
|
||||
function let(ExecutionContextInterface $context): void
|
||||
{
|
||||
$this->beConstructedWith(self::VALIDATION_GROUPS);
|
||||
$this->initialize($context);
|
||||
}
|
||||
|
||||
function it_is_a_constraint_validator(): void
|
||||
{
|
||||
$this->shouldHaveType(ConstraintValidator::class);
|
||||
}
|
||||
|
||||
function it_throws_exception_when_constraint_is_not_catalog_promotion_action_group(
|
||||
CatalogPromotionActionInterface $action,
|
||||
Constraint $constraint,
|
||||
): void {
|
||||
$this
|
||||
->shouldThrow(UnexpectedTypeException::class)
|
||||
->during('validate', [$action, $constraint])
|
||||
;
|
||||
}
|
||||
|
||||
function it_throws_exception_when_value_is_not_catalog_promotion_action(): void
|
||||
{
|
||||
$this
|
||||
->shouldThrow(UnexpectedTypeException::class)
|
||||
->during('validate', [new \stdClass(), new CatalogPromotionActionGroup()])
|
||||
;
|
||||
}
|
||||
|
||||
function it_does_nothing_when_type_is_null(
|
||||
ExecutionContextInterface $context,
|
||||
CatalogPromotionActionInterface $action,
|
||||
): void {
|
||||
$action->getType()->willReturn(null);
|
||||
|
||||
$context->getValidator()->shouldNotBeCalled();
|
||||
|
||||
$this->validate($action, new CatalogPromotionActionGroup());
|
||||
}
|
||||
|
||||
function it_does_nothing_when_type_is_an_empty_string(
|
||||
ExecutionContextInterface $context,
|
||||
CatalogPromotionActionInterface $action,
|
||||
): void {
|
||||
$action->getType()->willReturn('');
|
||||
|
||||
$context->getValidator()->shouldNotBeCalled();
|
||||
|
||||
$this->validate($action, new CatalogPromotionActionGroup());
|
||||
}
|
||||
|
||||
function it_passes_configured_validation_groups_for_further_validation(
|
||||
ExecutionContextInterface $context,
|
||||
ValidatorInterface $validator,
|
||||
ContextualValidatorInterface $contextualValidator,
|
||||
ConstraintViolationListInterface $violationList,
|
||||
CatalogPromotionActionInterface $action,
|
||||
): void {
|
||||
$constraint = new CatalogPromotionActionGroup();
|
||||
|
||||
$action->getType()->willReturn('test');
|
||||
|
||||
$context->getValidator()->willReturn($validator);
|
||||
$validator->inContext($context)->willReturn($contextualValidator);
|
||||
|
||||
$contextualValidator
|
||||
->validate($action, null, ['test_group'])
|
||||
->shouldBeCalled()
|
||||
->willReturn($contextualValidator)
|
||||
;
|
||||
|
||||
$context->getViolations()->willReturn($violationList);
|
||||
$violationList->count()->willReturn(1);
|
||||
|
||||
$contextualValidator
|
||||
->validate($action, new CatalogPromotionAction(null, $constraint->groups), ['test_group'])
|
||||
->shouldNotBeCalled()
|
||||
;
|
||||
|
||||
$this->validate($action, $constraint);
|
||||
}
|
||||
|
||||
function it_falls_back_to_previous_abstraction_when_no_violation_has_been_added(
|
||||
ExecutionContextInterface $context,
|
||||
ValidatorInterface $validator,
|
||||
ContextualValidatorInterface $contextualValidator,
|
||||
ConstraintViolationListInterface $violationList,
|
||||
CatalogPromotionActionInterface $action,
|
||||
): void {
|
||||
$constraint = new CatalogPromotionActionGroup();
|
||||
|
||||
$action->getType()->willReturn('test');
|
||||
|
||||
$context->getValidator()->willReturn($validator);
|
||||
$validator->inContext($context)->willReturn($contextualValidator);
|
||||
|
||||
$contextualValidator
|
||||
->validate($action, null, ['test_group'])
|
||||
->willReturn($contextualValidator)
|
||||
->shouldBeCalled()
|
||||
;
|
||||
|
||||
$context->getViolations()->willReturn($violationList);
|
||||
$violationList->count()->willReturn(0);
|
||||
|
||||
$contextualValidator
|
||||
->validate($action, new CatalogPromotionAction(null, $constraint->groups), ['test_group'])
|
||||
->shouldBeCalled()
|
||||
->willReturn($contextualValidator)
|
||||
;
|
||||
|
||||
$this->validate($action, $constraint);
|
||||
}
|
||||
|
||||
function it_falls_back_to_previous_abstraction_when_no_validation_group_for_the_action_type_has_been_passed(
|
||||
ExecutionContextInterface $context,
|
||||
ValidatorInterface $validator,
|
||||
ContextualValidatorInterface $contextualValidator,
|
||||
CatalogPromotionActionInterface $action,
|
||||
): void {
|
||||
$constraint = new CatalogPromotionActionGroup();
|
||||
|
||||
$action->getType()->willReturn('not_existing_type');
|
||||
|
||||
$context->getValidator()->willReturn($validator);
|
||||
$validator->inContext($context)->willReturn($contextualValidator);
|
||||
|
||||
$contextualValidator->validate($action, null, Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$context->getViolations()->shouldNotBeCalled();
|
||||
|
||||
$contextualValidator
|
||||
->validate($action, new CatalogPromotionAction(null, $constraint->groups), $constraint->groups)
|
||||
->shouldBeCalled()
|
||||
->willReturn($contextualValidator)
|
||||
;
|
||||
|
||||
$this->validate($action, $constraint);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\PromotionBundle\Validator;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionActionType;
|
||||
use Sylius\Component\Promotion\Model\CatalogPromotionActionInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
|
||||
|
||||
final class CatalogPromotionActionTypeValidatorSpec extends ObjectBehavior
|
||||
{
|
||||
private const ACTION_TYPES = [
|
||||
'test',
|
||||
'another_test',
|
||||
];
|
||||
|
||||
function let(ExecutionContextInterface $context): void
|
||||
{
|
||||
$this->beConstructedWith(self::ACTION_TYPES);
|
||||
$this->initialize($context);
|
||||
}
|
||||
|
||||
function it_is_a_constraint_validator(): void
|
||||
{
|
||||
$this->shouldHaveType(ConstraintValidator::class);
|
||||
}
|
||||
|
||||
function it_throws_exception_when_constraint_is_not_catalog_promotion_action_type(
|
||||
Constraint $constraint,
|
||||
CatalogPromotionActionInterface $action,
|
||||
): void {
|
||||
$this
|
||||
->shouldThrow(UnexpectedTypeException::class)
|
||||
->during('validate', [$action, $constraint])
|
||||
;
|
||||
}
|
||||
|
||||
function it_throws_exception_when_value_is_not_catalog_promotion_action(): void
|
||||
{
|
||||
$this
|
||||
->shouldThrow(UnexpectedTypeException::class)
|
||||
->during('validate', [new \stdClass(), new CatalogPromotionActionType()])
|
||||
;
|
||||
}
|
||||
|
||||
function it_does_nothing_when_passed_action_has_null_as_type(
|
||||
ExecutionContextInterface $context,
|
||||
CatalogPromotionActionInterface $action,
|
||||
): void {
|
||||
$action->getType()->willReturn(null);
|
||||
|
||||
$context->buildViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this->validate($action, new CatalogPromotionActionType());
|
||||
}
|
||||
|
||||
function it_does_nothing_when_passed_action_has_an_empty_string_as_type(
|
||||
ExecutionContextInterface $context,
|
||||
CatalogPromotionActionInterface $action,
|
||||
): void {
|
||||
$action->getType()->willReturn('');
|
||||
|
||||
$context->buildViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this->validate($action, new CatalogPromotionActionType());
|
||||
}
|
||||
|
||||
function it_does_nothing_when_catalog_promotion_action_has_valid_type(
|
||||
ExecutionContextInterface $context,
|
||||
CatalogPromotionActionInterface $action,
|
||||
): void {
|
||||
$action->getType()->willReturn('test');
|
||||
|
||||
$context->buildViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this->validate($action, new CatalogPromotionActionType());
|
||||
}
|
||||
|
||||
function it_adds_violation_when_type_is_unknown(
|
||||
ExecutionContextInterface $context,
|
||||
ConstraintViolationBuilderInterface $violationBuilder,
|
||||
CatalogPromotionActionInterface $action,
|
||||
): void {
|
||||
$constraint = new CatalogPromotionActionType();
|
||||
$action->getType()->willReturn('not_existing_type');
|
||||
|
||||
$context->buildViolation($constraint->invalidType)->willReturn($violationBuilder);
|
||||
$violationBuilder
|
||||
->setParameter('{{ available_action_types }}', implode(', ', self::ACTION_TYPES))
|
||||
->willReturn($violationBuilder)
|
||||
;
|
||||
$violationBuilder->atPath('type')->willReturn($violationBuilder);
|
||||
$violationBuilder->addViolation()->shouldBeCalled();
|
||||
|
||||
$this->validate($action, $constraint);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ declare(strict_types=1);
|
|||
namespace spec\Sylius\Bundle\PromotionBundle\Validator;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Bundle\PromotionBundle\Validator\CatalogPromotionAction\ActionValidatorInterface;
|
||||
use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionAction;
|
||||
use Sylius\Component\Promotion\Model\CatalogPromotionActionInterface;
|
||||
|
|
@ -44,16 +45,13 @@ final class CatalogPromotionActionValidatorSpec extends ObjectBehavior
|
|||
$this->shouldHaveType(ConstraintValidator::class);
|
||||
}
|
||||
|
||||
function it_adds_violation_if_catalog_promotion_action_has_invalid_type(
|
||||
function it_does_nothing_when_passed_action_type_has_no_validators(
|
||||
ExecutionContextInterface $executionContext,
|
||||
ConstraintViolationBuilderInterface $constraintViolationBuilder,
|
||||
CatalogPromotionActionInterface $action,
|
||||
): void {
|
||||
$action->getType()->willReturn('wrong_type');
|
||||
$action->getType()->willReturn('custom');
|
||||
|
||||
$executionContext->buildViolation('sylius.catalog_promotion_action.invalid_type')->willReturn($constraintViolationBuilder);
|
||||
$constraintViolationBuilder->atPath('type')->willReturn($constraintViolationBuilder);
|
||||
$constraintViolationBuilder->addViolation()->shouldBeCalled();
|
||||
$executionContext->buildViolation(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this->validate($action, new CatalogPromotionAction());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -403,6 +403,12 @@ final class CatalogPromotionsTest extends JsonApiTestCase
|
|||
'/api/v2/admin/channels/WEB',
|
||||
],
|
||||
'actions' => [
|
||||
[
|
||||
'type' => '',
|
||||
'configuration' => [
|
||||
'amount' => 0.5,
|
||||
],
|
||||
],
|
||||
[
|
||||
'type' => 'invalid_type',
|
||||
'configuration' => [
|
||||
|
|
@ -413,6 +419,12 @@ final class CatalogPromotionsTest extends JsonApiTestCase
|
|||
'type' => PercentageDiscountPriceCalculator::TYPE,
|
||||
'configuration' => [],
|
||||
],
|
||||
[
|
||||
'type' => PercentageDiscountPriceCalculator::TYPE,
|
||||
'configuration' => [
|
||||
'amount' => null,
|
||||
],
|
||||
],
|
||||
[
|
||||
'type' => PercentageDiscountPriceCalculator::TYPE,
|
||||
'configuration' => [
|
||||
|
|
@ -429,12 +441,6 @@ final class CatalogPromotionsTest extends JsonApiTestCase
|
|||
'type' => FixedDiscountPriceCalculator::TYPE,
|
||||
'configuration' => [],
|
||||
],
|
||||
[
|
||||
'type' => FixedDiscountPriceCalculator::TYPE,
|
||||
'configuration' => [
|
||||
'WEB' => [],
|
||||
],
|
||||
],
|
||||
[
|
||||
'type' => FixedDiscountPriceCalculator::TYPE,
|
||||
'configuration' => [
|
||||
|
|
@ -446,6 +452,16 @@ final class CatalogPromotionsTest extends JsonApiTestCase
|
|||
[
|
||||
'type' => FixedDiscountPriceCalculator::TYPE,
|
||||
'configuration' => [
|
||||
'WEB' => [],
|
||||
'MOBILE' => [],
|
||||
],
|
||||
],
|
||||
[
|
||||
'type' => FixedDiscountPriceCalculator::TYPE,
|
||||
'configuration' => [
|
||||
'MOBILE' => [
|
||||
'amount' => null,
|
||||
],
|
||||
'WEB' => [
|
||||
'amount' => 'text',
|
||||
],
|
||||
|
|
@ -471,11 +487,64 @@ final class CatalogPromotionsTest extends JsonApiTestCase
|
|||
], \JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/catalog_promotion/post_catalog_promotion_with_invalid_actions_response',
|
||||
Response::HTTP_UNPROCESSABLE_ENTITY,
|
||||
);
|
||||
$this->assertJsonResponseViolations($this->client->getResponse(), [
|
||||
[
|
||||
'propertyPath' => 'actions[0].type',
|
||||
'message' => 'Please choose an action type.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[1].type',
|
||||
'message' => 'Catalog promotion action type is invalid. Available types are fixed_discount, percentage_discount.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[2].configuration[amount]',
|
||||
'message' => 'This field is missing.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[3].configuration[amount]',
|
||||
'message' => 'The percentage discount amount must be a number and can not be empty.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[4].configuration[amount]',
|
||||
'message' => 'The percentage discount amount must be between 0% and 100%.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[5].configuration[amount]',
|
||||
'message' => 'This value should be a valid number.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[6].configuration[MOBILE]',
|
||||
'message' => 'This field is missing.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[6].configuration[WEB]',
|
||||
'message' => 'This field is missing.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[7].configuration[MOBILE]',
|
||||
'message' => 'This field is missing.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[7].configuration[WEB]',
|
||||
'message' => 'This field is missing.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[8].configuration[WEB][amount]',
|
||||
'message' => 'This field is missing.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[8].configuration[MOBILE][amount]',
|
||||
'message' => 'This field is missing.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[9].configuration[MOBILE][amount]',
|
||||
'message' => 'This value should not be blank.',
|
||||
],
|
||||
[
|
||||
'propertyPath' => 'actions[9].configuration[WEB][amount]',
|
||||
'message' => 'This value should be of type numeric.',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
{
|
||||
"@context": "/api/v2/contexts/ConstraintViolationList",
|
||||
"@type": "ConstraintViolationList",
|
||||
"hydra:title": "An error occurred",
|
||||
"hydra:description": "actions[0].type: Catalog promotion action type is invalid. Please choose a valid type.\nactions[1].configuration.amount: The percentage discount amount must be a number and can not be empty.\nactions[2].configuration.amount: The percentage discount amount must be between 0% and 100%.\nactions[3].configuration.amount: The percentage discount amount must be a number and can not be empty.\nactions[4].configuration: Provided configuration contains errors. Please add the fixed discount amount that is a number greater than 0.\nactions[5].configuration: Provided configuration contains errors. Please add the fixed discount amount that is a number greater than 0.\nactions[6].configuration: Configuration for one of the required channels is not provided.\nactions[7].configuration: Provided configuration contains errors. Please add the fixed discount amount that is a number greater than 0.",
|
||||
"violations": [
|
||||
{
|
||||
"propertyPath": "actions[0].type",
|
||||
"message": "Catalog promotion action type is invalid. Please choose a valid type.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "actions[1].configuration.amount",
|
||||
"message": "The percentage discount amount must be a number and can not be empty.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "actions[2].configuration.amount",
|
||||
"message": "The percentage discount amount must be between 0% and 100%.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "actions[3].configuration.amount",
|
||||
"message": "The percentage discount amount must be a number and can not be empty.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "actions[4].configuration",
|
||||
"message": "Provided configuration contains errors. Please add the fixed discount amount that is a number greater than 0.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "actions[5].configuration",
|
||||
"message": "Provided configuration contains errors. Please add the fixed discount amount that is a number greater than 0.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "actions[6].configuration",
|
||||
"message": "Configuration for one of the required channels is not provided.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "actions[7].configuration",
|
||||
"message": "Provided configuration contains errors. Please add the fixed discount amount that is a number greater than 0.",
|
||||
"code": null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
{
|
||||
"@context": "/api/v2/contexts/ConstraintViolationList",
|
||||
"@type": "ConstraintViolationList",
|
||||
"hydra:title": "An error occurred",
|
||||
"hydra:description": "scopes[0].type: Catalog promotion scope type is invalid. Please choose a valid type.\nscopes[1].configuration.variants: Please add at least 1 variant.\nscopes[2].configuration.variants: Please add at least 1 variant.\nscopes[3].configuration.variants: Provided configuration contains errors. Please add only existing variants.\nscopes[4].configuration.products: Provided configuration contains errors. Please add at least 1 product.\nscopes[5].configuration.products: Provided configuration contains errors. Please add at least 1 product.\nscopes[6].configuration.products: Provided configuration contains errors. Please add only existing products.\nscopes[7].configuration.taxons: Provided configuration contains errors. Please add at least 1 taxon.\nscopes[8].configuration.taxons: Provided configuration contains errors. Please add at least 1 taxon.\nscopes[9].configuration.taxons: Provided configuration contains errors. Please add only existing taxons.",
|
||||
"violations": [
|
||||
{
|
||||
"propertyPath": "scopes[0].type",
|
||||
"message": "Catalog promotion scope type is invalid. Please choose a valid type.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "scopes[1].configuration.variants",
|
||||
"message": "Please add at least 1 variant.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "scopes[2].configuration.variants",
|
||||
"message": "Please add at least 1 variant.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "scopes[3].configuration.variants",
|
||||
"message": "Provided configuration contains errors. Please add only existing variants.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "scopes[4].configuration.products",
|
||||
"message": "Provided configuration contains errors. Please add at least 1 product.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "scopes[5].configuration.products",
|
||||
"message": "Provided configuration contains errors. Please add at least 1 product.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "scopes[6].configuration.products",
|
||||
"message": "Provided configuration contains errors. Please add only existing products.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "scopes[7].configuration.taxons",
|
||||
"message": "Provided configuration contains errors. Please add at least 1 taxon.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "scopes[8].configuration.taxons",
|
||||
"message": "Provided configuration contains errors. Please add at least 1 taxon.",
|
||||
"code": null
|
||||
},
|
||||
{
|
||||
"propertyPath": "scopes[9].configuration.taxons",
|
||||
"message": "Provided configuration contains errors. Please add only existing taxons.",
|
||||
"code": null
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue