Home > Mobile >  Symfony constraint collection between values
Symfony constraint collection between values

Time:06-07

I am looking for a way to validate one array obtained via a form.

$arr = [13, 64];

The array is composed of 2 integers and I need to check that these integers are between 0 ad 1000, but also the 2nd integer is greater than the first AND the difference cannot exceed 100. The only only constraint I could put was the one validating the number with a know pattern :

'constraints' => new Assert\Collection([
     'fields' => [
           0 => new Assert\Range([
                'min' => 0,
                'max' => 1000,
           ]),
           1 => new Assert\Range([
                'min' => 0,
                'max' => 1000,
           ]),
      ]
 ]),

What is missing in my validation:

 $arr = [13, 64]; => should be correct
 $arr = [140, 64]; => not correct, $arr[0] > $arr[1]
 $arr = [13, 340]; => not correct, ($arr[1] - $arr[0]) > 100

I couldnt find in the symfony doc how to validate array fields among each others, and don't event know if there is a way to.

If anyone has a tip, you're welcome :)

thank you and cheers !

CodePudding user response:

Create a Custom Form Constraint.

Create 2 files and place them in src/Validator

The Constraint:

// src/Validator/CheckArray.php

namespace App\Validator;

use Symfony\Component\Validator\Constraint;

class CheckArray extends Constraint
{
    public $type = 'One of the values is not an integer.';
    public $range = 'One of the values is not within range. (0 and 1000)';
    public $exceeded = 'Value mismatched. Exceeded expected value.';
}

The Validator:

// src/Validator/CheckArrayValidator.php

namespace App\Validator;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;

class CheckArrayValidator extends ConstraintValidator
{
    public function validate($array, Constraint $constraint)
    {
        if (!$constraint instanceof CheckArray) {
            throw new UnexpectedTypeException($constraint, CheckArray::class);
        }

        if (null === $array || '' === $array) {
            return;
        }

        if (!is_array($array)) {
            throw new UnexpectedValueException($array, 'array');
        }

        if ($this->checkType()) {
            $this->context->buildViolation($constraint->type)->addViolation();
        }

        if ($this->checkRange()) {
            $this->context->buildViolation($constraint->range)->addViolation();
        }

        if ($this->checkExceeded()) {
            $this->context->buildViolation($constraint->exceeded)->addViolation();
        }
    }

    private function checkType(array $array): bool
    {
        return $array !== array_filter($array, 'is_int');
    }

    private function checkRange(array $array): bool
    {
        return $array !== array_filter($array, function($v){
            return ($v > 0 && $v < 1000);
        });
    }

    private function checkExceeded(array $array): bool
    {
        if ($array[0] > $array[1]) return true;
        if (($array[1] - $array[0]) > 100) return true;

        return false;
    }
}

In your form:

use App\Validator\CheckArray;

'constraints' => new CheckArray()

Not fully tested, tweak naming and messages as you need or add more.

  • Related