如何在Symfony2验证器中允许空值

27
我希望在对象属性上应用验证器,仅当值不为空时才进行验证。现在是 Symfony 的标准行为:

我想在一个对象的属性上应用验证器,只有在该属性的值非空时才应用验证器。

现在 Symfony 的标准行为:

class Entity
{
    /**
     * @ORM\Column(type="string", nullable=true)
     * @Assert\Email()
     */
    protected $email;
    (...)
}

如果电子邮件为null或空字符串,那么该对象将无法通过验证。是否有一种方法告诉验证器将空值断言为有效,并仅在字段具有数据时进行验证?

PS:我知道我可以编写回调验证器,但为了拥有“允许为空”功能而为每个字段编写回调验证器并不是很好。

6个回答

28

对于所有可选字段,在您的 FormBuilder 类中必须明确设置'required' => false。这里有一个描述字段类型选项的段落。


编辑。得到了很多反对票。默认情况下,所有验证器都将null值视为有效,除了NotNullNotEmpty。其中两者在问题中没有被使用。该问题隐含了如何关闭默认打开的客户端required属性。


29
这不是正确答案。'required'选项只强制执行客户端验证。 - Vlad
7
@TyrionLannister,你说得没错。然而,默认验证器中没有一个会将空值视为无效,除非使用了NotNull()NotEmpty()约束条件。鉴于提问者没有使用这两个约束条件之一,所以问题很可能是由于客户端的HTML5 required属性引起的。 - kgilden
3
@gilden,恕我直言,“required”属性与空值批准没有任何关系。它仅用于HTML5客户端验证,与验证(即服务器端验证)无关。 - Andrzej Ośmiałowski
2
@AndrzejOśmiałowski 完全正确,请查看您上方的评论。那正是我所说的 :) - kgilden
如果他根本不知道实现设置NotNull或NotEmpty约束的正确方法怎么办?我发现这个解释很有趣。即便如此,当他尝试通过XHR以JSON格式发送表单数据时,我相信他将会后悔自己犯了多少错误。表单将被传递,但你将得到空值。这正是我现在所面临的问题。这只是一个部分答案,而不是完整的答案。 - tom10271

20

required选项设置为true并不是解决方案:

请注意,将required选项设置为true不会导致应用服务器端验证。换句话说,如果用户提交了该字段的空值(例如使用旧版浏览器或Web服务),它将被接受为有效值,除非您使用Symfony的NotBlank或NotNull验证约束。

http://symfony.com/doc/current/book/forms.html#field-type-options

对于我的自定义验证器,我添加了一个

if (null == $value) {
    return true;
}

对于isValid()方法,我不确定标准验证器类的最佳方式是什么。


1
这应该是被接受的答案,因为被接受的答案仅适用于简单字段,而不适用于嵌入式类型字段。 - pscheit

3
如果我理解正确,您希望只有在输入值时进行服务器端验证。我正处于同样的情况。我想要验证一个URL,只有在提供了URL时才会进行验证。我找到的最好方法是编写自己的自定义验证类。您可以编写一个通用的自定义验证类。
我按照这个链接https://symfony-docs-chs.readthedocs.org/en/2.0/cookbook/validation/custom_constraint.html 进行操作,除了一些因为Symfony最新版本而进行的更改。以下是实现方式: Acme\BundleNameBundle\Validator\Constraints\cstmUrl
namespace Acme\BundleNameBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Url;

/**
 * @Annotation
 */
class CstmUrl extends Url
{
    public $message = 'The URL "%string%" is not valid';
}

Acme\BundleNameBundle\Validator\Constraints\cstmUrlValidator 的翻译是:自定义URL验证器。
namespace Acme\BundleNameBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Url;
use Symfony\Component\Validator\Constraints\UrlValidator;

class CstmUrlValidator extends UrlValidator
{
    public function validate($value, Constraint $constraint)
    {
        if(!$value || empty($value))
            return true;

        parent::validate($value, $constraint);
    }
}

Validtion.yml

Acme\BundleNameBundle\Entity\Student:
    Url:
        - Acme\BundleNameBundle\Validator\Constraints\CstmUrl: ~

在控制器中,只需绑定您通常会执行的约束即可。

'constraints'=> new CstmUrl(array("message"=>"Invalid url provided"))

我相信还有其他更好的方法来完成它,但现在我觉得这样做很好。


2

以防其他人遇到同样的问题,我更喜欢通过在我的Twig模板中添加以下属性来个人解决此问题:

{{ form_row(form.<field>, {'required': false}) }}

1
我认为你不想在模板中硬编码这个。如果想使用required方法,请使用gilden的建议,将其添加到表单构建器中。 - Luke
在大多数情况下,我认为这将是更好的方法(这样属性验证要求就会传播到其他模板),但在某些情况下(即简单应用程序),将其放入Twig模板中也不太糟糕 :) - Anton Babushkin

1
这里是我实际发现的技巧:

https://github.com/symfony/symfony/issues/5906

你需要编写一个什么也不做的数据转换器,然后将其添加到你的字段中。之后,只需使用第二个参数为false调用submit()方法即可。
请注意,这在Symfony 2.3中是可以的。

0

字段类型猜测处理此事。 http://symfony.com/doc/current/book/forms.html#field-type-guessing

它取决于您的表单声明:“当您省略add()方法的第二个参数(或者将null传递给它)时,会激活“猜测”。如果您将选项数组作为第三个参数传递(如上面的dueDate),则这些选项将应用于猜测的字段。”


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接