ZF2 - 验证器链未在空元素上运行

时间:2013-01-22 09:08:55

标签: validation zend-framework2 xor

我创建了一个自定义验证器,需要填写“门牌号码”或“固定号码”,但不能同时填写(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'验证器针对我的两个元素运行,尽管两个字段都是空的?

3 个答案:

答案 0 :(得分: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;
}

由于allowEmpty对于我的输入是正确的,该输入的验证将停止并移至数组中的下一个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现在是假的,所以我的链中会自动添加一个'NotEmpty'验证器。一旦发现,解决方案很简单。我在自定义库中创建了一个“输入”类,它覆盖了injectNotEmptyValidator函数,强制它不执行任何操作:

namespace Si\InputFilter;

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

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

希望有所帮助。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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