依赖于另一个字段的约束注释以验证一个字段

huangapple go评论69阅读模式
英文:

Constraint annotation to validate one field depending on another field

问题

这是Symfony/5.4中的实体类代码:

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

class Assignments
{
    public const SALARY_RANGES = [
        'Red',
        'Green',
        null,
    ];

    /**
     * @ORM\Column(length=255, nullable=true)
     * @Assert\Choice(choices=Assignments::SALARY_RANGES, strict=true)
     */
    private ?string $salaryRange;

    /**
     * @ORM\ManyToOne(targetEntity="Employee", inversedBy="assignments")
     * @ORM\JoinColumn(name="employee_id", referencedColumnName="id", onDelete="CASCADE")
     */
    private ?Employee $employee;
}

我需要确保salaryRange仅在employee不为null时才具有非null值。是否可以使用约束注解来强制执行这一点?

我一直在尝试使用@Assert\Callback,但我无法弄清楚如何获取另一个字段的值。也许这甚至不是正确的工具。

/**
 * @Assert\Callback({"ExampleValidator", "assertEmployeeOnlyCallback"})
 */
public static function assertEmployeeOnlyCallback($value, ExecutionContextInterface $context)
{
    // `$value` 包含来自 `salaryRange` 的值,但 `employee` 在哪里?
}
英文:

I have this entity class in Symfony/5.4:

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

class Assignments
{
    public const SALARY_RANGES = [
        'Red',
        'Green',
        null,
    ];

    /**
     * @ORM\Column(length=255, nullable=true)
     * @Assert\Choice(choices=Assignments::SALARY_RANGES, strict=true)
     */
    private ?string $salaryRange;

    /**
     * @ORM\ManyToOne(targetEntity="Employee", inversedBy="assignments")
     * @ORM\JoinColumn(name="employee_id", referencedColumnName="id", onDelete="CASCADE")
     */
    private ?Employee $employee;
}

I need to ensure that salaryRange has a non-null value if, and only if, employee is not null. Is it possible to enforce that with constraint annotations?

I've been playing with @Assert\Callback but I couldn't figure out how to get the value of the other field. Perhaps it's not even the right tool.

/**
 * @Assert\Callback({"ExampleValidator", "assertEmployeeOnlyCallback"})
 */

<!---->

public static function assertEmployeeOnlyCallback(mixed $data, ExecutionContextInterface $context): void
{
    // `$data` contains value from `salaryRange` but, where is `employee`?
}

答案1

得分: 1

只需按照文档操作:

https://symfony.com/doc/5.3/reference/constraints/Callback.html

class Author
{
    // ...
    private int $field = 1;
    private string $otherField;
    /**
     * @Assert\Callback
     */    
    public function validate(ExecutionContextInterface $context, mixed $payload): void
    {
        
        if ($this->field > 1 && $this->otherField != '') {
            $context->buildViolation('Your validation message')
                ->atPath('toherField')
                ->addViolation();
        }
    }
}
英文:

just follow the documentation

https://symfony.com/doc/5.3/reference/constraints/Callback.html

class Author
{
    // ...
    private int $field = 1;
    private string $otherField;
   /**
    * @Assert\Callback
    */    
    public function validate(ExecutionContextInterface $context, mixed $payload): void
    {
        
        if ($this-&gt;field &gt; 1 &amp;&amp; $this-&gt;otherField != &#39;&#39;) {
            $context-&gt;buildViolation(&#39;Your validation message&#39;)
                -&gt;atPath(&#39;toherField&#39;)
                -&gt;addViolation();
        }
    }
}

答案2

得分: -2

你可以使用一个 Expression 约束。如果表达式为 true,验证将通过。如果表达式为 false,将会出现违规。

可以写在单个断言中,但为了提供更准确的消息,你可以写几个:

/**
 * @ORM\Column(length=255, nullable=true)
 * @Assert\Expression(
 *     "value !== null || this.getEmployee() === null",
 *     message="This value should not be null for employee assignments."
 * )
 * @Assert\Expression(
 *     "value === null || this.getEmployee() !== null",
 *     message="This field can only be set for employee assignments."
 * )
 * @Assert\Choice(choices=Assignments::SALARY_RANGES, strict=true)
 */
private ?string $salaryRange = null;

在我的情况下,我还必须设置一个默认值,因为我正在使用 PHP 类型化属性,并且在初始化之前访问了它。

这种方法与使用属性约束一样强大(我认为它只是相同底层逻辑的不同接口),唯一的缺点是在注释中使用表达式语言编写逻辑可能比编写纯 PHP 代码更难维护和调试。

有关无法确定字段填充顺序的评论提到的警告不太有意义,因为这不是表单验证的工作方式:读取请求字段和应用验证是分开的步骤。文档中也没有这样的警告。

英文:

You can use an Expression constraint. If the expression is true, validation will pass. If the expression is false, there will be a violation.

It can be written in a single assertion, but you can write a couple of them in order to provide more accurate messages:

/**
 * @ORM\Column(length=255, nullable=true)
 * @Assert\Expression(
 *     &quot;value !== null || this.getEmployee() === null&quot;,
 *     message=&quot;This value should not be null for employee assignments.&quot;
 * )
 * @Assert\Expression(
 *     &quot;value === null || this.getEmployee() !== null&quot;,
 *     message=&quot;This field can only be set for employee assignments.&quot;
 * )
 * @Assert\Choice(choices=Assignments::SALARY_RANGES, strict=true)
 */
private ?string $salaryRange = null;

In my case, I also had to set a default value because I'm using a PHP typed property and it was accessed before being initialised.

This approach is as robust as using a property constraint (I presume it's just an different interface for the same underlying logic), the only drawback being that writing logic using Expression Language inside annotations is arguably harder to maintain and debug that writing pure PHP code.

The caveat mentioned in a comment about having no way to determine what order the fields are populated does not make too much sense because that is not how form validation works: reading request fields and applying validations happen in separate steps. There's no such warning in documentation either.

huangapple
  • 本文由 发表于 2023年7月17日 23:04:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76705792.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定