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