1

I have Item entity class, and I must validate it. I have condition that price must be less than 1000. And if price is less than 5, and stock is less 10 it is wrong too. So for first condition I use LessThan Constraint, but what should I do for the second condition?

class Item {
   /**
     * @ORM\Column(type="float")
     * @Assert\LessThan(self::ITEM_MAX_PRICE)
     */
    private float $price;

   /**
     * @ORM\Column(type="integer")
     */
    private int $stock;

}

How can I validate this both property's with second condition?

5
  • symfony.com/doc/current/validation/custom_constraint.html Commented Jan 5, 2021 at 19:51
  • I already read this, but it is about validation of one property, not it? Commented Jan 5, 2021 at 20:00
  • You can put assertions on the class as well. Commented Jan 5, 2021 at 20:06
  • In other words, when put on the class, you're interacting with the object (which the custom validator gives you access to). Commented Jan 5, 2021 at 20:16
  • It's domain, so personally I would recommend this is logic that should be implemented in domain code. I've added an example doing something similar from a project of mine. Commented Jan 5, 2021 at 20:25

4 Answers 4

2

I would use a custom validator. Here's an example from one of my projects (3.4).

Three files:

<?php // src/AppBundle/Validator/Constraint/HasValidDimensions.php

namespace AppBundle\Validator\Constraint;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class HasValidDimensions extends Constraint
{
    public $invalidWidth = 'Invalid width dimension.';
    public $invalidHeight = 'Invalid height dimension.';

    public function getTargets()
    {
        return self::CLASS_CONSTRAINT;
    }
}

Note the use of CLASS_CONSTRAINT. Now, the actual validator gives me access to the different parts (in this case it's a command validator):

<?php // src/AppBundle/Validator/Constraint/HasValidDimensionsValidator.php

namespace AppBundle\Validator\Constraint;

use AppBundle\Message\Command\PageTemplate\SetPageTemplateDimensions;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

/**
 * @Annotation
 */
class HasValidDimensionsValidator extends ConstraintValidator
{
    public function validate($setPageTemplateDimensions, Constraint $constraint): void
    {
        /* @var SetPageTemplateDimensions $setPageTemplateDimensions */
        /* @var HasValidDimensions $constraint */

        $pageTemplate = $setPageTemplateDimensions->getPageTemplate();

        if ($setPageTemplateDimensions->getWidth() > $pageTemplate->getWidthReal()) {
            $this->context
                ->buildViolation($constraint->invalidWidth)
                ->addViolation()
            ;
        }

        if ($setPageTemplateDimensions->getHeight() > $pageTemplate->getHeightReal()) {
            $this->context
                ->buildViolation($constraint->invalidHeight)
                ->addViolation()
            ;
        }
    }
}

Then use the validator on the class:

<?php // src/AppBundle/Message/Command/PageTemplate/SetPageTemplateDimensions.php

namespace AppBundle\Message\Command\PageTemplate;

use AppBundle\Validator\Constraint as AppAssert;

/**
 * @AppAssert\HasValidDimensions()
 */
class SetPageTemplateDimensions implements TenantOwnedInterface
{
Sign up to request clarification or add additional context in comments.

Comments

1

Unfortunately Symfony don't provide a build-in constraint to validate multiples properties between them (not so easy to do and maintain btw).

I suggest you to have a look to Callback constraint which allow flexibility to validate your entity.

But if your validation logic is growing I suggest you to do a custom validator. It's a service so you can inject services and test it easily.

Comments

0

May be it is good and will be useful to use Group sequence. In this case first will be check is price is less than and only if the first is true will be checked another two assertion Something like that:

   /** 
 * @Assert\GroupSequence({"Item", "Strict"}) 
 */
class Item
{

   /** 
     * @ORM\Column(type="float")         
     * @Assert\LessThan(self::ITEM_MAX_PRICE) 
     * @Assert\GreaterThan(value=5, groups={"Strict"})
     */ 
     private float $price; 

   /** 
     * @Assert\GreaterThan(value=10, groups={"Strict"})
     * @ORM\Column(type="integer") 
     */ 
     private int $stock;
}

2 Comments

Unfortunately, it checks condition one by one, not together :(
In this way may be it is good to make custom validator with class target :)
0

So, I think you need to look at this: https://medium.com/@gregurco.vlad/symfony-validation-trick-dependent-validation-eb48bc5e3d81

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.