-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Description
On our path towards configuring validation with PHP attributes (#38096), after #38309, #38382, #38410 and #38499, composite constraints are still open. I'd like to work on them for the 5.3 release and therefore continue the discussion that we started on #38309.
As a reference, those are the constraints that we're talking about.
All
AtLeastOneOf
Collection
Compound
(abstract)Existence
(abstract)Required
Optional
Sequentially
All those constraints need a set of nested constraints as input. As discussed earlier, those are a bit tricky because PHP 8.0 does not allow us to nest attributes. If we want to use attributes here as well, we need to work around that limitation.
While most of the constraints receive the nested constraints as simple array, Collection
however requires a mapping (field to constraint) and is usually combined with other composite constraints, which gives us a second nesting level.
Let's discuss the options that we have.
Pray and hope for PHP 8.1 to deliver nested attributes
That's the easiest one for us! Let's do an RFC.
Introduce an end marker and flatten the nesting
Suggested by @nicolas-grekas (#38309 (comment)):
#[Assert\All,
Assert\NotNull,
Assert\Length(max: 6),
EndAssert]
In this case, our AnnotationLoader
would traverse the constraints following a composite until it finds the EndAssert
attribute. Those attributes would then be funnelled into the property labelled as Composite::getCompositeOption()
. The only case where is approach won't work out of the box would be Collection
because we would just generate an ordered list this way, not a mapping like Collection
requires.
Allow the list of constraints to be passed as callable
#[Assert\All(
constraints: [MyConstraintFactory::class, 'createConstraints'],
)]
While constructing the constraint, the callable would be resolved.
This would be an easy workaround, but of course it defeats the main reason one would want to use attributes: explicit readable configuration at the class/method/property that is to be configured.
Introduce an array convention for nested constraints
#[Assert\All(
constraints: [
[Assert\NotNull::class],
[Assert\Length::class, 'max' => 6],
]
)]
When constructing the constraint, each of the arrays would be replaced by the corresponding constructed constraint. This would be easily doable, however we would lose IDE support for the nested constraint declarations.
Document workarounds
A possible workaround would be building an own constraint that extends and configures a composite constraint. We can document how to do this. If we expect nested attributes to be implemented eventually, those workarounds could be enough for the time being.