【问题标题】:How to validate some custom constraint in symfony 5如何在 symfony 5 中验证一些自定义约束
【发布时间】:2025-12-16 15:25:02
【问题描述】:

我有 Item 实体类,我必须验证它。我的条件是价格必须小于 1000。如果价格小于 5,库存小于 10,那也是错误的。所以对于第一个条件我使用 LessThan Constraint,但是对于第二个条件我应该怎么做呢?

class Item {
   /**
     * @ORM\Column(type="float")
     * @Assert\LessThan(self::ITEM_MAX_PRICE)
     */
    private float $price;

   /**
     * @ORM\Column(type="integer")
     */
    private int $stock;

}

如何验证这两个属性的第二个条件?

【问题讨论】:

  • 我已经读过这篇文章,但它是关于一个属性的验证,不是吗?
  • 你也可以在类上放置断言。
  • 换句话说,当你进入类时,你正在与对象(自定义验证器允许你访问)进行交互。
  • 它是域,所以我个人建议这是应该在域代码中实现的逻辑。我添加了一个与我的项目类似的示例。

标签: php symfony validation


【解决方案1】:

不幸的是,Symfony 没有提供内置约束来验证它们之间的多个属性(顺便说一句,这样做和维护并不容易)。

我建议您查看Callback 约束,它可以灵活地验证您的实体。

但是,如果您的验证逻辑正在增长,我建议您使用custom validator。它是一项服务,因此您可以轻松地注入服务并对其进行测试。

【讨论】:

    【解决方案2】:

    我会使用自定义验证器。这是我的一个项目 (3.4) 中的一个示例。

    三个文件:

    <?php // src/AppBundle/Validator/Constraint/HasValidDimensions.php
    
    namespace AppBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    
    /**
     * @Annotation
     */
    class HasValidDimensions extends Constraint
    {
        public $invalidWidth = 'Invalid width dimension.';
        public $invalidHeight = 'Invalid height dimension.';
    
        public function getTargets()
        {
            return self::CLASS_CONSTRAINT;
        }
    }
    

    注意CLASS_CONSTRAINT 的使用。现在,实际的验证器让我可以访问不同的部分(在这种情况下,它是一个命令验证器):

    <?php // src/AppBundle/Validator/Constraint/HasValidDimensionsValidator.php
    
    namespace AppBundle\Validator\Constraint;
    
    use AppBundle\Message\Command\PageTemplate\SetPageTemplateDimensions;
    use Symfony\Component\Validator\Constraint;
    use Symfony\Component\Validator\ConstraintValidator;
    
    /**
     * @Annotation
     */
    class HasValidDimensionsValidator extends ConstraintValidator
    {
        public function validate($setPageTemplateDimensions, Constraint $constraint): void
        {
            /* @var SetPageTemplateDimensions $setPageTemplateDimensions */
            /* @var HasValidDimensions $constraint */
    
            $pageTemplate = $setPageTemplateDimensions->getPageTemplate();
    
            if ($setPageTemplateDimensions->getWidth() > $pageTemplate->getWidthReal()) {
                $this->context
                    ->buildViolation($constraint->invalidWidth)
                    ->addViolation()
                ;
            }
    
            if ($setPageTemplateDimensions->getHeight() > $pageTemplate->getHeightReal()) {
                $this->context
                    ->buildViolation($constraint->invalidHeight)
                    ->addViolation()
                ;
            }
        }
    }
    

    然后在类上使用验证器:

    <?php // src/AppBundle/Message/Command/PageTemplate/SetPageTemplateDimensions.php
    
    namespace AppBundle\Message\Command\PageTemplate;
    
    use AppBundle\Validator\Constraint as AppAssert;
    
    /**
     * @AppAssert\HasValidDimensions()
     */
    class SetPageTemplateDimensions implements TenantOwnedInterface
    {
    

    【讨论】:

      【解决方案3】:

      使用组序列可能很好并且很有用。在这种情况下,首先将检查价格是否小于,并且仅当第一个为真时才会检查另外两个断言 类似的东西:

         /** 
       * @Assert\GroupSequence({"Item", "Strict"}) 
       */
      class Item
      {
      
         /** 
           * @ORM\Column(type="float")         
           * @Assert\LessThan(self::ITEM_MAX_PRICE) 
           * @Assert\GreaterThan(value=5, groups={"Strict"})
           */ 
           private float $price; 
      
         /** 
           * @Assert\GreaterThan(value=10, groups={"Strict"})
           * @ORM\Column(type="integer") 
           */ 
           private int $stock;
      }
      

      【讨论】:

      • 不幸的是,它一个一个地检查条件,而不是一起检查:(
      • 以这种方式制作带有类目标的自定义验证器可能会很好:)
      【解决方案4】: