-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Validator] Use composite constraints as attributes #38503
Comments
Even I don't know much about these advanced constraints, I'll keep insisting about flattening the config instead of hoping for nested attributes. Instead of: #[Assert\All,
Assert\NotNull,
Assert\Length(max: 6),
EndAssert] What if we move the #[Assert\NotNull(all: true)]
#[Assert\Length(all: true, max: 6)] If not possible, in https://github.com/doctrine/orm/pull/8266/files#diff-de70f2dceca06a63629901e912f4982e I've seen an interesting usage of "sequential attributes". Would this work? #[Assert\All, Assert\NotNull]
#[Assert\All, Assert\Length(max: 6)] |
This notation would require us to add a #[Assert\NotNull(options: ['all' => true])] However, I don't see how this works for multiple nesting levels: /**
* @Assert\AtLeastOneOf({
* @Assert\All({ … a set of constraints …}),
* @Assert\All({ … another set of constraints …}),
* })
*/ We need some way to link a nested attribute to the correct container.
Grouping attributes like this is pure syntax sugar. If I query those attributes via reflection, I would just get four attributes in order. I cannot determine the boundaries of each |
The syntax with EndAssert also works best with multiple levels of nested attributes. |
True. Do you have an idea how we could map the |
does PHP guarantee that the order of attributes is preserved ?
This also looses additional features ( |
My own vote would be to stick to native syntax (i.e. wait for native nesting support). The hack based on
|
We don‘t really know, if that feature will get implemented. We would make a bet here.
Composite constraints do already have a common base class that can and should be used by userland constraints as well. And we can even query that base class for the name of the property that holds the nested constraints.
I agree, that‘s a bummer.
The IDE won‘t tell us that we forgot the end attribute. Apart from that, IDE support should work. |
Well, if nested attributes never make it into the core, I would say that this means that attributes are not a good fit for applying complex validation constraints, and we should rather embrace that fact fully.
The IDE won't help you figuring out the nesting either. Try a case where you have multiple constraints applied on a property, including one which is a composite one (or even worse, nested composite constraints). The IDE won't know about the nesting, and so won't help you getting it right. |
Btw, does anyone has an answer about whether PHP guarantees the order of attributes when returning them with Reflection ? if the answer is "no", the |
I'm not sure I agree here. Attributes are as good as Doctrine Annotations for that task. Don't get me wrong, I'm not saying that we have to implement any of the workarounds. I just want to get the arguments on the table to get to a good decision.
Yeah, and I don't see a problem with making the usage of the base class a requirement if someone wants to enable their own constraint for usage as attribute.
That is correct. The only guidance that we could give the user here is through meaningful error messages.
I have re-read the RFC and did not find anything about it. My personal observation is that the attributes are in fact returned in order and my expectation would be that it remains that way. I have also found a test that would break if the attributes would be returned in a different order: https://github.com/php/php-src/blob/php-8.0.0rc1/Zend/tests/attributes/028_grouped.phpt But you are right: There is no documented guarantee that the behavior won't change in the future. We should clarify this first, otherwise this option would be off the table. |
I quite like the array alternative proposed in the issue description, along with waiting for native support. This method seems very doable and not hard to implement. Also, it is easy to add native nested attribute support to this alternative afaics.
|
@wouterj the problem I see with that syntax is that you usually define multiple constraints, so you'll have the #[Assert\All(
constraints: [
[Assert\Length::class, 'max' => 6],
]
)]
#[Assert\Length(max: 6)] What's the problem? That we are mixing different syntaxes for the same thing. Sometimes it's |
I would take the order as a property we can rely on. Unlike Go, PHP preserves the order of hash maps. We'll run tests, PHP also has tests, and I'm pretty sure they alreay rely on the order. |
This variant would be very easy to migrate once we have nested attributes.
Yes, that might get confusing.
That's something that you will need to get used to. There are reasonable non-hacky use-cases for arrays in attribute declarations, e.g.:
or
|
We can have a base class or an interface. We can also imagine a syntax dedicated to nested constraints:
We don't have to use
But for nested constraints, the IDE will work as expected. Their options are discoverable, unlike when using I don't know how this proposal works for the |
we can also decide to go for an all-arrays syntax, with only one attribute:
The benefit of this syntax is that we could revert all the code we added in constraint classes to support both styles of annotations (docblock+attributes). The new |
3rd and last comment: we could decide to NOT support nested constraints when using attributes, and contribute to php-internals to add support for them. |
We could even use an attribute for that. 🙃
This syntax gets messy if we want to modify the properties of the composite constraint itself. I'd prefer to use the composite constraint itself as start marker.
Right. The reflection API should give us everything we need to do the constructor call ourselves and it would be pretty straightforward to insert the nested attributes into the constructor call.
I think this thread makes a very good argument: There is need for nesting and the lack of it leads to undesirable workarounds in userland. I could help with writing the necessary RFC, but I'd need assistance since I'm unfamiliar with the process around it. Also, my C skills are… a bit rusty. |
Not allowing the nested attributes is a huge flaw in PHP. We should fix that. It is the only good solution to this problem. |
Came here to leave my 2 cents after reading the blog post. Waiting for nested attributes to be added to PHP is fine IMHO. Everything else feels a bit hacky and not a proper solution. Support for annotations won't be dropped until attributes reach feature parity (I believe) so it's fine to keep a bit of the old until the new can 100% replace it, otherwise I'm afraid people would migrate it to a temporary solution and then have to change it once more after nested attributes finally land in PHP. |
Having a way, in a constraint class, to define a composition of nested constraints could be a simple first iteration that would allow:
I admit that my last point is opinionated but this solution seems to be a straightforward trade-off. |
Given that nested annotations support, if the RFC even gets accepted and implemented; will not get till 8.1 at least which will be at nearly 2021 year ends means that Symfony will need to wait till 5.4 => 6.0 this means two new versions without this feature. I agree that will be de ideal path but it feels too long. The
Feels ok imo and yes it's a complete diff way of doing it comparing it with its annotation counterpart but at least we will be able to use it. And why not introduce a cleaner more PHP native way next year when the syntax gets delivered. |
This is the best way to go about this, imo. Some of the proposed solutions look very good, but all of them have significant drawbacks. Helping PHP reach nested attributes would have the least impact in current codebases that rely on Doctrine's nested annotations. Asking people to update their code twice may lead to dev dissatisfaction. |
Some of the PHP tests rely on the order such as this one: https://github.com/php/php-src/blob/master/Zend/tests/attributes/004_name_resolution.phpt#L36 But I also agree with other comments, just as in PHP it was decided to not deal with the extra complexity of nested attributes in the first round, I think its fine to continue without them in Symfony, and continue to use a mix of native and doctrine attributes for the time being. |
Encouraging PHP to allow nested attributes in 8.1 is the best option, but this will take some time and only be available in a year at the earliest. Being able to replace all annotations with attributes in the near future seems like a big advantage that would be worth two extra attributes, and they are very similar to a possible nested attributes syntax. |
…wkania) This PR was squashed before being merged into the 5.2 branch. Discussion ---------- [Validator] Document constraints as php 8 Attributes We already have [examples](https://github.com/symfony/symfony-docs/pull/14230/files) of code for the PHP attributes. We also have [Constraints as php 8 Attributes](symfony/symfony#38309) and [2](#14305). The rest will be implemented in the [future](symfony/symfony#38503). Commits ------- 748bd54 [Validator] Document constraints as php 8 Attributes
This RFC probably solves the issue... https://wiki.php.net/rfc/new_in_initializers.
|
Indeed! php/php-src#6746 |
But also for PHP 8.1 earliest 😉 |
It seems like nested attributes are now implemented in PHP 8.1: https://wiki.php.net/rfc/new_in_initializers I think we can safely convert these last remaining constraints to attributes? |
Yes, that's possible now, see #42102 |
…e-daubois) This PR was merged into the 5.4 branch. Discussion ---------- [Validator] Add support of nested attributes | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #38503 | License | MIT | Doc PR | symfony/symfony-docs#15541 Although the RFC (https://wiki.php.net/rfc/new_in_initializers) is in the voting phase (until 14 July), it is already well on its way to passing. Based on `@nikic`'s development (php/php-src#7153), this makes the development of support possible. It will obviously take a little while before this pull request is merged. If this pull request is OK for you, I'll get to work on writing the existing documentation for the attribute validation constraints. ![Capture d’écran du 2021-07-05 17-11-23](https://user-images.githubusercontent.com/2144837/124491886-0d2f7d80-ddb4-11eb-8147-493bdc6c48ac.png) Not sure about the Symfony version to target, as `AtLeastOneOf` has been introduced in 5.1. Although, I couldn't find attributes validation documentation for 4.4. Commits ------- 1449450 [Validator] Add support of nested attributes for composite constraints
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)):
In this case, our
AnnotationLoader
would traverse the constraints following a composite until it finds theEndAssert
attribute. Those attributes would then be funnelled into the property labelled asComposite::getCompositeOption()
. The only case where is approach won't work out of the box would beCollection
because we would just generate an ordered list this way, not a mapping likeCollection
requires.Allow the list of constraints to be passed as callable
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
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.
The text was updated successfully, but these errors were encountered: