英文:
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->field > 1 && $this->otherField != '') {
$context->buildViolation('Your validation message')
->atPath('toherField')
->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(
* "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;
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论