Как установить пользовательское сообщение об ошибке для обязательного поля формы в Zend Framewor 2?

В моем Form у меня есть Fieldset, который содержит два элемента foo и bar. Бизнес-правило для них заключается в том, что один должен быть установлен. Таким образом, набор полей действителен, когда установлено foo ИЛИ bar, и недействителен, когда никто не установлен.

Я решил это следующим образом:

public function getInputFilterSpecification()
{
    return [
        'foo' => [
            'required' => empty($this->get('bar')->getValue())
        ],
        'bar' => [
            'required' => empty($this->get('foo')->getValue())
        ],
    ];
}

Работающий. Но все еще есть проблема с сообщениями об ошибках: если поля бота пусты, пользователь получает для каждого поля сообщение «Требуется значение и не может быть пустым». Тогда пользователь думает, что он должен заполнить оба поля.

Как настроить сообщение об ошибке для поля required, чтобы отображались правильные сообщения, такие как "Требуется значение для foo и не может быть пустым, если полоса не установлена". и "Значение для bar обязательно и не может быть пустым, если foo не установлен."?


person automatix    schedule 05.11.2016    source источник


Ответы (2)


Вы можете написать собственный ValidatorChain, расширив цепочку по умолчанию

Затем вы можете переопределить этот метод:

/**
 * Returns true if and only if $value passes all validations in the chain
 *
 * Validators are run in the order in which they were added to the chain  (FIFO).
 *
 * @param  mixed $value
 * @param  mixed $context Extra "context" to provide the validator
 * @return bool
 */
 public function isValid($value, $context = null)
 { .. }

По умолчанию этот метод вернет true, только если ВСЕ валидаторы вернут true, но он также имеет доступ к контексту — это означает, что вы также можете получить значения всех других полей.

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

Затем вы просто прикрепляете все поля, которые хотите сделать «одним из обязательных».

person Andrew    schedule 07.11.2016

Вероятно, вы получите собственный валидатор, подобный этому:

class MyCustomValidator extends ZendAbstractValidator
{
    const FIELDS_EMPTY = 'fieldsEmpty';

    /**
     * Error messages
     *
     * @var array
     */
    protected $abstractMessageTemplates = [
        self::FIELDS_EMPTY => "Vale for %field1% is required and can't be empty, if %field2% is not set.",
    ];

    /**
     * Variables which can be used in the message templates
     *
     * @var array
     */
    protected $abstractMessageVariables = [
        'field1' => 'field1',
        'field2' => 'field2',
    ];

    /**
     * Value of the field
     * @var mixed
     */
    protected $value;

    /**
     * Name of the first field to check, which the validator is bind to
     * @var mixed
     */
    protected $field1;

    /**
     * Name of the second field to check
     * @var string
     */
    protected $field2;

    /**
     * MyCustomValidator constructor.
     *
     * @param array|null|\Traversable $options
     *
     * @throws \Exception
     */
    public function __construct($options)
    {
        if ($options instanceof Traversable) {
            $options = ArrayUtils::iteratorToArray($options);
        }

        if (!array_key_exists('field1', $options) || !array_key_exists('field2', $options)) {
            throw new \Exception('Options should include both fields to be defined within the form context');
        }

        $this->field1 = $options['field1'];
        $this->field2 = $options['field2'];

        parent::__construct($options);
    }

    /**
     * Returns true if and only if $value meets the validation requirements
     * If $value fails validation, then this method returns false, and
     * getMessages() will return an array of messages that explain why the
     * validation failed.
     *
     * @param  mixed $value
     * @param array  $context
     *
     * @return bool
     */
    public function isValid($value, $context = [])
    {
        $this->setValue($value);

        if (empty($value) && (array_key_exists($this->field2, $context) || empty($context[$this->field2]))) {
            $this->error(self::FIELDS_EMPTY);

            return false;
        }

        return true;
    }
}

Итак, как его использовать:

public function getInputFilterSpecification()
{
    return [
        'foo' => [
            'validators' => [
                [
                    'name' => MyCustomValidator::class,
                    'options' => [
                        'field1' => 'foo',
                        'field2' => 'bar',
                    ]
                ]
            ]
        ],
        'bar' => [
            'validators' => [
                [
                    'name' => MyCustomValidator::class,
                    'options' => [
                        'field1' => 'bar',
                        'field2' => 'foo',
                    ]
                ]
            ]
        ],
    ];
}

Для тех, кто не знает, как зарегистрировать MyCustomValidator — в вашем module.config.php или используйте public function getValidatorConfig() в вашем Module.php. Не используйте оба, это одно или другое:

Как зарегистрироваться в вашем module.config.php

'validators' => array(
    'factories' => array(
        MyCustomValidator::class => MyCustomValidatorFactory::class,
    ),
 ),

Как зарегистрироваться в вашем module.php:

/**
 * Expected to return \Zend\ServiceManager\Config object or array to
 * seed such an object.
 * @return array|\Zend\ServiceManager\Config
 */
public function getValidatorConfig()
{
    return [
        'aliases' => [
            'myCustomValidator' => MyCustomValidator::class,
            'MyCustomValidator' => MyCustomValidator::class,
            'mycustomvalidator' => MyCustomValidator::class,
        ],
        'factories' => [
            MyCustomValidator::class => MyCustomValidatorFactory::class,
        ],
    ];
}

Фабричный класс:

class MyCustomValidatorFactory implements FactoryInterface, MutableCreationOptionsInterface
{
    /**
     * Options for the InputFilter
     *
     * @var array
     */
    protected $options;

    /**
     * Create InputFilter
     *
     * @param ServiceLocatorInterface $serviceLocator
     *
     * @return BlockChangeOnSerialsValidator
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        return new MyCustomValidator($this->options);
    }

    /**
     * Set creation options
     *
     * @param  array $options
     *
     * @return void
     */
    public function setCreationOptions(array $options)
    {
        $this->setOptions($options);
    }

    /**
     * Set options
     *
     * @param array $options
     */
    public function setOptions(array $options)
    {
        $this->options = $options;
    }
}

Обратите внимание, что я оставил метод валидатора isValid() довольно простым, так как не уверен, что он подходит для вашего случая, но это поможет вам двигаться в правильном направлении. Но улучшение, которое можно сделать, заключается в повторном использовании валидатора NotEmpty для проверки того, является ли поле пустым или нет.

Обратите внимание, что Context внутри формы isValid($value, $context = null) является formData при вызове $form->setData($this->getRequest()->getPost()).

person Kwido    schedule 14.11.2016
comment
Спасибо за ваш ответ, но он не работает в случае с условно обязательными полями, которые я описал в вопросе. Пожалуйста, прочитайте вопрос. - person automatix; 14.11.2016
comment
Ааа ладно. Я вижу, что немного поторопился с выводом, вероятно, из-за того, что вы выделили «Как настроить сообщение об ошибке». Постараюсь обновить свой ответ, так как он, вероятно, придет к пользовательскому валидатору. - person Kwido; 15.11.2016
comment
@automatix Помог ли вам мой обновленный ответ? Поскольку вопрос все еще остается без ответа, вы уже нашли для него решение? - person Kwido; 25.11.2016