-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Form] Add ability to clear form errors #27580
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
Conversation
ff852b9
to
10985d3
Compare
* | ||
* @return $this | ||
*/ | ||
public function clearErrors($deep = false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could true
be a better default here? How frequent is to clear errors for root form only? I think most of the time it happens inside a child field.
There is an issue about the same default but getErrors($deep = false)
method #25738 and was reported as confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a strong preference either way, but I do think the default value here should match getErrors()
.
I have now thought about this, what about adding a |
src/Symfony/Component/Form/Form.php
Outdated
// Clear errors from children | ||
foreach ($this as $child) { | ||
if ($child instanceof ClearableErrorInterface) { | ||
$child->clearErrors(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to have a clearable form type nested in a non-clearable one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, buttons are not clearable and they could be included in a form that is clearable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The question was the other way round. As the code here stops the traversal of the form tree as soon as a non-clearable form type is encountered, it will not clear clearable fields nested in non-clearable ones.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only non-clearable fields we have in the symfony core are buttons that can't have children.
This could append in libraries but then it can be considered the responsibility of the library to implement this interface if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I know. But why risk it, if the change would be fairly easy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would mean children won't be able to implement custom error cleaning logic, that's something someone might want to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I know. But why risk it, if the change would be fairly easy?
We can't add clearErrors()
to FormInterface
as that would be a BC break. And even if we could, that would require us to implement clearErrors()
on the Button
class, which makes no sense because buttons can't have errors or children - it'll either be a pointless no-op or throw a BadMethodCallException
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't hinting at implementing it everywhere, but decoupling the traversal of the form tree from the actual form field clearing.
Like the visitor pattern, if you will.
But this edge case doesn't seem too severe.
* | ||
* @return $this | ||
*/ | ||
public function clearErrors($deep = false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add type hint to argument public function clearErrors(bool $deep = false);
.
But please don't add : self
, we already had discussion on this (#27190 (comment)).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added and amended! Thanks for the feedback 👍
f365b92
to
f86e407
Compare
If the method is |
f86e407
to
17c427e
Compare
Good idea @vudaltsov, I like that better! I have made that change. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Looking forward to AJAX partial form reloading without errors workarounds 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thanks!
* | ||
* @return $this | ||
*/ | ||
public function clearErrors(bool $deep = false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should type hint : self
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@HeahDude , we had a discussion on return types here: #27190 (comment)
The problem will be that when using ->clearErrors()->
on actual object implementing this interface, the IDE will not show any other methods in autocomplete, because : self
is not : static
. So from my opinion it's much better to leave @return $this
phpdoc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can leave the @return $this
phpdoc and add : self
at the same time. AFAIK phpdoc takes precedence with type declaration for IDE auto completion, this means we would get both the correct auto completion and strictness at the language level (that an instance of the interface is returned).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, then let's use : self
😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using : self
on an interface forces the Form
subclass implementation to use a : ClearableErrorsInterace
typehint:
Fatal error: Declaration of Symfony\Component\Form\Form::clearErrors(bool $deep = false): Symfony\Component\Form\Form must be compatible with Symfony\Component\Form\ClearableErrorsInterface::clearErrors(bool $deep = false): Symfony\Component\Form\ClearableErrorsInterface in src/Symfony/Component/Form/Form.php on line 61
I'm not sure this is what we want.
AFAIK phpdoc takes precedence with type declaration for IDE auto completion
I could not get this working in the latest version of PhpStorm:
Furthermore, I could not find any other interfaces in Symfony with a : self
return type hint. For these reasons I think we should leave it as-is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact that we don't have any existing interfaces with a self
return type hint is probably because we couldn't make use of them before Symfony 4. So I would still use it here and use the docblock to be more precise.
/** | ||
* Removes all the errors of this form. | ||
* | ||
* @param bool $deep whether to remove errors from child forms as well |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Symfony code base uses capital style to comment params.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, amended, and force-pushed :) Thanks for catching that!
17c427e
to
5c1a657
Compare
src/Symfony/Component/Form/Form.php
Outdated
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function clearErrors(bool $deep = false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing return type hint (same in the interface)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually I am not even sure if it makes sense that this method returns anything
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All form methods are chainable, so it makes sense to keep it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's time to decide this once and for all in https://symfony.com/doc/current/contributing/code/standards.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, fluent methods are a pain for return types. so even if we want to have as much return types as possible for new APIs, I'm not sure we want them for fluent APIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xabbuh @stof I have removed return $this;
from the method for a few reasons:
- You're right, this method doesn't really need to be chainable.
- ClearableTokenStorageInterface (a new and similar interface introduced in SF4) does not return anything, so perhaps we should be consistent here.
- It seems like there's no definitive rule on how to type hint interface methods that return
$this
/self
. I don't want to set a bad precedent here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then you should add : void
in interface and class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I am for consistency with FormInterface
which has @return $this
on methods like addError
. Although I agree that fluent interfaces are pain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed my mind and added return $this;
back in. I could see developers wanting to call $form->clearErrors()->createView()
so this definitely has value.
The last unanswered question is what to do about return types. Our ultimate goal is to have Form::clearErrors()
return that original Form
instance. Here's what the Return Type Declarations RFC has to say about our current situation:
The enforcement of the declared return type during inheritance is invariant; this means that when a sub-type overrides a parent method then the return type of the child must exactly match the parent and may not be omitted.
This is why using : self
on the interface requires the Form
method to use : ClearableErrorsInterface
which is not ideal.
However, the RFC goes on to say:
If the parent does not declare a return type then the child is allowed to declare one.
Because this is a valid approach, I think omitting the return type from the interface but using : self
on the Form
makes the most sense in this situation:
- A return type on the interface is pointless because there are literally no other methods on
ClearableErrorsInterface
. Is somebody really going to call$form->clearErrors()->clearErrors()->clearErrors()
? - This gives us the functionality we want (accurate return type hint on
Form
) despite PHP's current limitations with invariant return types.
c69e507
to
ed74efd
Compare
* | ||
* @return $this | ||
*/ | ||
public function clearErrors(bool $deep = false): self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xabbuh I have added a : self
return type here per your request. See #27580 (comment) for an explanation of why I didn't also add a return type to the interface. Let me know your thoughts :)
Tests are failing, but I don't think it's because of my changes. All tests under |
ed74efd
to
9eb755c
Compare
Thank you @colinodell. |
This PR was squashed before being merged into the 4.2-dev branch (closes #27580). Discussion ---------- [Form] Add ability to clear form errors | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #14060 | License | MIT | Doc PR | symfony/symfony-docs#9916 This PR adds the ability to manually clear form errors, thus improving the DX issue reported in #14060. Unlike my original approach in #14233 and #27571 which break BC, this adds a new `ClearableErrorInterface` which `Form` implements. (`Button` does not implement it because buttons can't have errors.) Commits ------- 9eb755c [Form] Add ability to clear form errors
Changelog added in a1eb649 (forgot to comment before merging) |
…uiluz) This PR was merged into the master branch. Discussion ---------- [Form] Ability to clear form errors Documenting the new feature proposed in symfony/symfony#27580 Commits ------- f07f536 Wrap long lines and add the versionadded directive 7a4465d Clarify that clearing errors makes the form valid d4143f1 [Form] Ability to clear form errors
This PR adds the ability to manually clear form errors, thus improving the DX issue reported in #14060.
Unlike my original approach in #14233 and #27571 which break BC, this adds a new
ClearableErrorInterface
whichForm
implements. (Button
does not implement it because buttons can't have errors.)