如何验证依赖于 Symfony 2 中另一个属性的属性

Posted

技术标签:

【中文标题】如何验证依赖于 Symfony 2 中另一个属性的属性【英文标题】:How to validate a property dependent on another property in Symfony 2 【发布时间】:2012-08-28 22:46:43 【问题描述】:

是否可以验证模型类的属性依赖于同一类的另一个属性?

例如,我有这个类:

class Conference

    /** $startDate datetime */
    protected $startDate;

    /** $endDate datetime */
    protected $endDate;

我希望 Symfony 2.0 验证,$startDate 必须在 $endDate 之后。

这可以通过注释实现还是我必须手动执行?

【问题讨论】:

【参考方案1】:

Symfony 2.4 开始,您还可以使用Expression 验证约束来实现您所需要的。我相信,这是最简单的方法。肯定比回调约束更方便。

以下是如何使用验证约束注释更新模型类的示例:

use Symfony\Component\Validator\Constraints as Assert;


class Conference

    /**
     * @var \DateTime
     *
     * @Assert\Expression(
     *     "this.startDate <= this.endDate",
     *     message="Start date should be less or equal to end date!"
     * )
     */
    protected $startDate;

    /**
     * @var \DateTime
     *
     * @Assert\Expression(
     *     "this.endDate >= this.startDate",
     *     message="End date should be greater or equal to start date!"
     * )
     */
    protected $endDate;

不要忘记在您的项目配置中enable annotations。

您始终可以使用expression syntax 进行更复杂的验证。

【讨论】:

Thos 不适用于 Symfony 4 Cannot access protected property App\Entity\Reservation::$start_at【参考方案2】:

是的,回调验证器:http://symfony.com/doc/current/reference/constraints/Callback.html

在 symfony 2.0 上:

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContext;

/**
 * @Assert\Callback(methods="isDateValid")
 */
class Conference


    // Properties, getter, setter ...

    public function isDateValid(ExecutionContext $context)
    
        if ($this->startDate->getTimestamp() > $this->endDate->getTimestamp()) 
                $propertyPath = $context->getPropertyPath() . '.startDate';
                $context->setPropertyPath($propertyPath);
                $context->addViolation('The starting date must be anterior than the ending date !', array(), null);
        
    

在 symfony 主版本上:

    public function isDateValid(ExecutionContext $context)
    
        if ($this->startDate->getTimestamp() > $this->endDate->getTimestamp()) 
            $context->addViolationAtSubPath('startDate', 'The starting date must be anterior than the ending date !', array(), null);
        
    

这里我选择在 startDate 字段上显示错误消息。

【讨论】:

标记了@Psykehoe 的答案,这应该是评论而不是答案,因此在这里改写他的评论。 addViolationAtSubPath 自 2.2 版起已弃用,请改用 addViolationAt。【参考方案3】:

另一种方法(至少从 Symfony 2.3 开始)是使用简单的@Assert\IsTrue

class Conference

    //...

    /**
     * @Assert\IsTrue(message = "Startime should be lesser than EndTime")
     */
    public function isStartBeforeEnd()
    
        return $this->getStartDate() <= $this->getEndDate;
    

    //...

作为参考,documentation。

【讨论】:

@Assert\True 自 Symfony 2.0 以来就已经存在,但是您应该使用 Symfony 2.3 中的 @Assert\IsTrue @DanielP:是的,2.3 添加了“@Assert\IsTrue”。 '@Assert\True' 从 2.7 中弃用。已更新,谢谢。【参考方案4】:

自从version 2.4 以来,它变得更加简单。您所要做的就是将此方法添加到您的类中:

use Symfony\Component\Validator\Context\ExecutionContextInterface;

/**
 * @Assert\Callback
 */
public function isStartBeforeEnd(ExecutionContextInterface $context)

    if ($this->getStartDate() <= $this->getEndDate()) 
        $context->buildViolation('The start date must be prior to the end date.')
                ->atPath('startDate')
                ->addViolation();
    

buildViolation method 返回一个构建器,该构建器具有几个其他方法来帮助您配置约束(如参数和翻译)。

【讨论】:

【参考方案5】:

更好更清洁的解决方案https://symfony.com/doc/3.4/validation/custom_constraint.html 是写

自定义约束(基本上是错误消息) 及其验证器(类似于执行控制的控制器函数

要检查实体是否正常,请添加到自定义约束(不是验证器)

public function getTargets()

    return self::CLASS_CONSTRAINT;

这允许您使用该实体的实例而不仅仅是属性值。这使得在验证器中写入成为可能:

public function validate($object, Constraint $constraint)

    #Your logic, for example:
    if($value1 = $object->getValue1())
    
        if($value2 = $object->getValue2())
        
            if($value1 === $value2)
            
                # validation passed
                return True;
            
            else
            
                # validation failed
                $this->context->buildViolation($constraint->message)
                    ->setParameter(' string ', $value1.' !== '.$value2)
                    ->addViolation();
            

最好的部分是你需要在实体类中写的:

use YourBundle\Validator\Constraints as YourAssert;

/**
 * Yourentity
 *
 * @ORM\Table(name="yourentity")
 * @ORM\Entity(repositoryClass="YourBundle\Repository\YourentityRepository")
 *
 * @YourAssert\YourConstraintClassName # <-- as simple as this

希望有帮助

【讨论】:

【参考方案6】:

对于日期验证,我们可以简单地使用 GreaterThan 和 GreaterThanOrEqual 比较约束。

class Conference

     /**
     * @var \DateTime
     * @Assert\GreaterThanOrEqual("today")
     */
    protected $startDate;

     /**
     * @var \DateTime
     * @Assert\GreaterThan(propertyPath="startDate")
     */
    protected $endDate;

欲了解更多信息,请参阅validation constraints

【讨论】:

如果您直接使用字符串作为“today”,它可以工作,但对于“startDate”,它需要明确使用 propertyPath,如 here 所述

以上是关于如何验证依赖于 Symfony 2 中另一个属性的属性的主要内容,如果未能解决你的问题,请参考以下文章

使表的显示依赖于 Interface Builder 中另一个表中的选择

Symfony 验证 - 在自定义验证行为中设置属性路径

为啥断言跳过验证我的 symfony5 表单用户名字段?

Symfony2验证器约束大于其他属性

依赖于其他属性的 Grails 验证

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