Подтвердить что ты не робот

Как я могу проверить ключи массива с помощью Symfony Validation?

Как я могу проверить ключи массива с помощью проверки Symfony?

Скажем, у меня есть следующее, и каждый ключ массива emails - это идентификатор. Как я могу проверить их с помощью обратного вызова или какого-либо другого ограничения (скажем, например, ограничение регулярного выражения, а не обратный вызов)?

$input = [
    'emails' => [
        7 => '[email protected]',
        12 => '[email protected]',
    ],
    'user' => 'bob',
    'amount' => 7,
];

use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints;
$validator = Validation::createValidator();
$constraint = new Constraints\Collection(array(
    'emails' => new Constraints\All(array(
        new Constraints\Email(),
    )),
    'user' => new Constraints\Regex('/[a-z]/i'),
    'amount' => new Constraints\Range(['min' => 5, 'max' => 10]),
));

$violations = $validator->validateValue($input, $constraint);
echo $violations;

(используя последний dev-master symfony)

4b9b3361

Ответ 1

Я бы создал пользовательское ограничение проверки, которое применяет ограничения для каждой пары ключ-значение (или ключ, только если вы хотите) в массиве. Подобно ограничению All, но проверка выполняется по паре ключ-значение, а не только по значению.

namespace GLS\DemoBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;

class AssocAll extends Constraint
{
    public $constraints = array();

    public function __construct($options = null)
    {
        parent::__construct($options);

        if (! is_array($this->constraints)) {
            $this->constraints = array($this->constraints);
        }

        foreach ($this->constraints as $constraint) {
            if (!$constraint instanceof Constraint) {
                throw new ConstraintDefinitionException('The value ' . $constraint . ' is not an instance of Constraint in constraint ' . __CLASS__);
            }
        }
    }

    public function getDefaultOption()
    {
        return 'constraints';
    }

    public function getRequiredOptions()
    {
        return array('constraints');
    }
}

Валидатор ограничений, который передает массив с парой ключ-значение каждому ограничению:

namespace GLS\DemooBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;


class AssocAllValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
        if (null === $value) {
            return;
        }

        if (!is_array($value) && !$value instanceof \Traversable) {
            throw new UnexpectedTypeException($value, 'array or Traversable');
        }

        $walker = $this->context->getGraphWalker();
        $group = $this->context->getGroup();
        $propertyPath = $this->context->getPropertyPath();

        foreach ($value as $key => $element) {
            foreach ($constraint->constraints as $constr) {
                $walker->walkConstraint($constr, array($key, $element), $group, $propertyPath.'['.$key.']');
            }
        }
    }
}

Я предполагаю, что только ограничение Callback имеет смысл применяться к каждой паре ключ-значение, где вы помещаете свою логику проверки.

use GLS\DemoBundle\Validator\Constraints\AssocAll;

$validator = Validation::createValidator();
$constraint = new Constraints\Collection(array(
    'emails' => new AssocAll(array(
        new Constraints\Callback(array(
            'methods' => array(function($item, ExecutionContext $context) {
                    $key = $item[0];
                    $value = $item[1];

                    //your validation logic goes here
                    //...
                }
            ))),
    )),
    'user' => new Constraints\Regex('/^[a-z]+$/i'),
    'amount' => new Constraints\Range(['min' => 5, 'max' => 10]),
));

$violations = $validator->validateValue($input, $constraint);
var_dump($violations);

Ответ 2

Существует ограничение обратного вызова. См. http://symfony.com/doc/master/reference/constraints/Callback.html

Update:

Я не мог найти более чистый способ проверки ключей текущего значения. Вероятно, это лучший способ, я не тратил слишком много времени на это, но он работает для вашего дела.

    use Symfony\Component\Validator\Constraints;
    use Symfony\Component\Validator\Context\ExecutionContextInterface;

    // ...

    $input = array(
    'emails' => array(
            7 => '[email protected]',
            12 => '[email protected]',
    ),
    'user' => 'bob',
    'amount' => 7,
    );

    // inside a sf2 controller: $validator = $this->get('validator.builder')->getValidator();
    $validator = Validation::createValidator();
    $constraint = new Constraints\Collection(array(
        'emails' => new Constraints\All(array(
                new Constraints\Email(),
                new Constraints\Callback(array('methods' => array(function($value, ExecutionContextInterface $context){
                    $propertyPath = $context->getPropertyPath();
                    $valueKey = preg_replace('/[^0-9]/','',$propertyPath);
                    if($valueKey == 7){
                        $context->addViolationAt('email', sprintf('E-Mail %s Has Has Key 7',$value), array(), null);
                    }
                })))
        )),
        'user' => new Constraints\Regex('/[a-z]/i'),
        'amount' => new Constraints\Range(array('min' => 5, 'max' => 10)),
    ));

    $violations = $validator->validate($input, $constraint);
    echo $violations;

Ответ 3

Вы можете использовать функцию php array_flip, чтобы отменить ключи, значения и использовать валидатор или пользовательский.

Надеюсь, что это будет полезно.

С уважением.

Ответ 4

Немного поздно, но вот вы, мой друг:

use Symfony\Component\Validator\Constraints as Assert;

public function getConstraints()
{
    return [
        'employee' => [new Assert\NotBlank(), new Assert\Type("integer")],
        'terms' => [new Assert\NotBlank(), new Assert\Type("array")],
        'pivotData' => new Assert\All([
            new Assert\Type("array"),
            new Assert\Collection([
                'amount' => new Assert\Optional(new Assert\Type('double'))
            ])
        ]),
    ];
}

Несколько вещей, чтобы заметить здесь:

  • В приведенном выше примере мы проверяем ключ pivotData. pivotData должен быть массивом дополнительных данных, которые мы хотим проверить.

  • Каждый раз, когда мы хотим проверить массив, мы начинаем с new Assert\All, что означает, что мы хотим проверить все в pivotData

  • Затем мы добавляем новый Assert\Type("array"), чтобы проверить, действительно ли массив передан из интерфейса.

  • И, самое главное, мы создаем new Assert\Collection, где мы определяем наши новые свойства поодиночке в качестве стандарта. В приведенном выше примере я добавил ключ amount, который представляет свойство pivotData. Вы можете свободно перечислить все свои свойства здесь, они будут проверены:)

Удачи:)