2

我创建了一个自定义验证器,需要填写“门牌号”或“单位号”,但不能同时填写(XOR)。

如果两个字段都已填写(失败)或一个字段已填写(通过),则验证器可以正常工作。但是如果两个字段都为空,则验证器根本不会运行。

这是两个元素的输入过滤器规范:

$input = new Input('flat_number');
$input->setRequired(true);
$input->setAllowEmpty(true);
$input->setValue($this->flatNumber);
$input->getValidatorChain()
        ->addValidator(new \Si\Validator\HouseFlatCheck('house_number'))
        ->addValidator(new \Zend\Validator\StringLength(array('max' => 30)));
$input->getFilterChain()->attach($this->defaultFilterChain);
$inputFilter->add($input);

$input = new Input('house_number');
$input->setRequired(true);
$input->setAllowEmpty(true);
$input->setValue($this->houseNumber);
$input->getValidatorChain()
        ->addValidator(new \Si\Validator\HouseFlatCheck('flat_number'))
        ->addValidator(new \Zend\Validator\StringLength(array('max' => 30)));
$input->getFilterChain()->attach($this->defaultFilterChain);
$inputFilter->add($input);

从表面上看,如果字段为空并且通过了“required”和“allowEmpty”要求,验证器就不会运行。

尽管两个字段都是空的,有什么方法可以让“HouseFlatCheck”验证器针对我的两个元素运行?

4

3 回答 3

3

经过一整天的研究,我找到了解决问题的方法。

首先,Zend\InputFilter\BaseInputFilter 类为 isValid() 包含以下内容:

public function isValid()
{
    if (null === $this->data) {
        throw new Exception\RuntimeException(sprintf(
            '%s: no data present to validate!',
            __METHOD__
        ));
    }

    $this->validInputs   = array();
    $this->invalidInputs = array();
    $valid               = true;

    $inputs = $this->validationGroup ?: array_keys($this->inputs);
    foreach ($inputs as $name) {
        $input = $this->inputs[$name];
        if (!array_key_exists($name, $this->data)
            || (null === $this->data[$name])
            || (is_string($this->data[$name]) && strlen($this->data[$name]) === 0)
        ) {
            if ($input instanceof InputInterface) {
                // - test if input is required
                if (!$input->isRequired()) {
                    $this->validInputs[$name] = $input;
                    continue;
                }
                // - test if input allows empty
                if ($input->allowEmpty()) {
                    $this->validInputs[$name] = $input;
                    continue;
                }
            }
            // make sure we have a value (empty) for validation
            $this->data[$name] = '';
        }

        if ($input instanceof InputFilterInterface) {
            if (!$input->isValid()) {
                $this->invalidInputs[$name] = $input;
                $valid = false;
                continue;
            }
            $this->validInputs[$name] = $input;
            continue;
        }
        if ($input instanceof InputInterface) {
            if (!$input->isValid($this->data)) {
                // Validation failure
                $this->invalidInputs[$name] = $input;
                $valid = false;

                if ($input->breakOnFailure()) {
                    return false;
                }
                continue;
            }
            $this->validInputs[$name] = $input;
            continue;
        }
    }

    return $valid;
}

由于我的 Input 的 allowEmpty 为 true,因此该 Input 的验证停止并继续移动到数组中的下一个 Input 元素。在我的 Input 上将 allowEmpty 更改为 false 可以解决该问题,但会创建另一个问题,因为 'Zend\InputFilter\Input' 类在其 isValid() 函数期间调用以下函数:

protected function injectNotEmptyValidator()
{
    if ((!$this->isRequired() && $this->allowEmpty()) || $this->notEmptyValidator) {
        return;
    }
    $chain = $this->getValidatorChain();

    // Check if NotEmpty validator is already first in chain
    $validators = $chain->getValidators();
    if (isset($validators[0]['instance'])
        && $validators[0]['instance'] instanceof NotEmpty
    ) {
        $this->notEmptyValidator = true;
        return;
    }

    $chain->prependByName('NotEmpty', array(), true);
    $this->notEmptyValidator = true;
}

因为我的字段是必需的并且 allowEmpty 现在为 false,所以会自动将“NotEmpty”验证器添加到我的链中。一旦发现,解决方案很简单。我在我的自定义库中创建了一个“输入”类,它覆盖了 injectNotEmptyValidator 函数,迫使它什么也不做:

namespace Si\InputFilter;

class Input extends \Zend\InputFilter\Input {
    protected function injectNotEmptyValidator() {
        return;
    }
}

执行上述操作,'NotEmpty' 不会添加到我的验证器链中,因此我的 XOR 验证器可以正确运行。我现在要做的就是使用我更正后的 Si\InputFilter\Input 类来为这两个元素填充 InputFilter。

希望有帮助。

于 2013-01-22T10:46:13.707 回答
1

我会以不同的方式执行此操作:我将创建一个自定义输入,该输入接受一组值,代表门牌号和公寓号。然后,输入将包含一个执行 XOR 逻辑的自定义验证器,它会根据存在的情况选择适当的验证链。(类似地,在表单方面,我将其表示为复合元素。)

于 2013-01-22T12:19:27.237 回答
1

从 2.2.0 开始,这似乎已被修复:https ://github.com/zendframework/zf2/pull/4165

于 2013-07-23T14:17:51.913 回答