Problem/Motivation
EntityForm currently does not actually save the entity, but instead makes every single implementation do that on its own.
Proposed resolution
Because core entity forms provide a proper drupal_set_message() and set a form redirect saved code is actually minimal (as it's literally one line that is being replaced with the parent::save() call). However, this change allows to actually use EntityForm as an actual form for development purposes or for custom modules, where an up-to-the-standards UX (i.e. a drupal_set_message() and a form redirect) is not necessarily required.
Many core forms do not yet follow the pattern of having a base form class which are extended by dedicated add and edit form classes. Instead they use the return value of EntityInterface::save() (SAVED_NEW or SAVED_UPDATED) to execute different logic. As this is no longer possible with this change - instead of hacking around the issue - the forms are converted to the new pattern.
As part of that instead of copying outdated code snippets into the new classes, the code is modernized along the way. This includes the following changes:
| Current version | Replacement |
|---|---|
t() |
$this->t() |
l() |
$this->getLinkGenerator()->generateFromUrl() |
node_type_get_label() |
$this->nodeTypeStorage->load()->label() |
These changes are important as reproducing the outdated code as part of the newly added classes would conflict with efforts to obsolete and drop support for said code.
Because entity delete forms of course do not want to save an entity but want to delete it, EntityDeleteFormBase and ContentEntityDeleteFormBase are introduced to centralize the deletion in save(). The confusion caused by a method called save() performing a deletion, is owed to the structure of EntityForm. This could easily be fixed however either as part of this issue or as a separate issue. It is a pre-existing condition, however, even if it is exposed through this issue.
As currently no forms call parent::save() not much would actually break if we did not change the forms. We would only have to fix forms which currently do not overide save() as for them the behavior changes, but those are not many. So if this patch is deemed to large to review and commit in a timely fashion any number of form conversions can be pushed to a follow-up.
Remaining tasks
Write patch.
User interface changes
None.
API changes
EntityForm::save() now saves the entity.
| Comment | File | Size | Author |
|---|---|---|---|
| #27 | 2312133-27-entity-form-save.patch | 328.62 KB | tstoeckler |
| #24 | 2312133-24-entity-form-save.patch | 160.21 KB | tstoeckler |
Comments
Comment #1
tstoecklerHere we go. This updates all config entity forms up to C, all content entity forms up to C and all content entity confirm forms. That is roughly a third of all entity forms.
Comment #3
tstoecklerHere we go. Re-rolled after #2225353: Convert $form_state to an object and provide methods like setError() and #2272481: Remove usages of watchdog() from forms, plugins and controllers.
This completes the content entity forms. I didn't touch TaxonomyForm, as it is extended by ForumForm and thus the usual add-edit pattern doesn't really work there. That is, it's totally doable, but it deserves it's own issue, as it is non-trivial and thus needs to discussed, etc. separately.
Let's see.
Comment #5
tstoecklerHere we go. This completes the rest of the forms.
I also started fixing a couple things from inspecting the test output above.
Uploading the interdiffs from 1 to 3 as well as from 3 to 5 here as I had forgotte them above. There are multiple interdiffs because of merges in between.
Comment #7
tstoecklerFinally figured the problem out. I was passing
$form_stateby reference all over the place butEntityForm::save()does not. PhpStorm could really be more helpful in such cases.Let's see what's left.
Comment #9
tstoecklerHere we go. This should be green, or at least almost green. (Yes, I ran all those locally, can I get a cookie... ;-))
I found a view conversions which are in fact not necessary for this issue, I will split those out into separate issues.
Also found a problem with ViewTestBase and XDebug cookie forwarding, will open a separate issue for that as well.
So check the "related issues" block, in case you're interested in that.
Edit: Darn, I forgot
-C -Mwhen generating the patch, that's why it's so huge. So don't bother reviewing it (or at least those parts) I will post a proper patch in case this is green.Comment #11
tstoecklerLet's see. Crossing my fingers.
Comment #13
tstoecklerMeh, run in circles much, @tstoeckler?!
Comment #15
tstoecklerAhh, so this conflicted with #2278567: Standardize node route names by relationship.
For anyone that still doesn't believe it: This is proof that converting stuff to new APIs while in the context of fixing other stuff (or moving around code, as it is here) is very important and in no way scope-creep.
Comment #16
tstoecklerHere's a self-review now that this passes. I will now also work on a "minimal" version of this patch, which excludes all changes that are not strictly necessary, now that the approach is proven to work for all forms, without much overhead (only 1 form in core needs to override save() to opt out of the saving).
Let's actually override actions() to provide a proper delete() submission handler.
Boo!
Boo!
Missing empty line.
Order seems important here. It needs to be verified that this still works.
It needs to be verified that this still works due to the different order.
Comment #17
tstoecklerHere we go.
Next step: Provide a "minimal" patch.
Comment #19
tstoecklerHere we go. ImageStyleDeleteForm::submit() was no longer being called after we renamed the submit handlers in EntityDeleteFormBase::actions().
Comment #20
slashrsm commentedWow. Impressive patch. One nitpik and two questions.
More a question... A lot of times I see this @todo-s in codebase and can't find follow-up issues. Do we have any policy about that? DO we open follow-ups when patch with @todo goes in or it is more relaxed kind of thing ("somebody should do it at some point in time" kind of thing)?
Whitespace.
What is the idea behind having empty classes?
Comment #21
tstoecklerThanks for the review!
1. Yeah, I was afraid someone would ask for that. Will open some follow-ups :-)
2. Noooo!!!!!!!!!! Will fix
3. Yeah, that's kind of weird. CommentForm can't be really be split as is because it doesn't provide any specialized messages or so. So I think it makes more sense to just leave those out and have both the 'add' and the 'edit' operation point to the same class (CommentForm).
Comment #22
marcingy commented+1 on this idea. I am wonder if stuff such as
ie moving files around should be done in a different patch to reduce the size of the patch some what.
Comment #23
berdirIs there any chance that we could make this an implementation that just works instead of an abstract class?
Not strictly related, agreed, but we're changing a lot already, and we have *many* of those delete forms that are all very similar, and mostly differ in slightly different messages and cancel/confirm redirect behavior and watchdog/dsm() messages.
Confirm messages already have a pattern for putting many things in different methods to make it easy to override them, we could extend that.
See also #2316171: [meta] Improve DX of entity defining (if you want a UI), which already links to this issue.
We could push this to a separate issue, but then it might be easier to extract all the delete related stuff from this issue and do that first, instead of changing all those classes twice?
Comment #24
tstoecklerFixed #20. See the related issues for the follow-ups I opened.
Had a stab at #23, that was a really great suggestion! Let me know what you think.
Re #22: I still on plan on providing a "minimal" version of this patch, but the problem is that many forms currently check the return value of
$this->entity->save()to decide whether it is adding or updating an entity. If we want to utilizeparent::save()that does not work anymore. So instead of replacing one workaround with another workaround with another one I'm fixing things properly. In the "minimal" patch I will just leave as many save() functions as possible untouched.Comment #25
tstoecklerOops, forgot to actually add the follow-ups to the related issues. :-)
Comment #27
tstoecklerConflicted on #1987882: Convert content_translation routes to a new style controller.
Comment #28
berdirLooks like the last patch is a diff against an older branch?
That looks wrong, urlInfo() already returns a Url object.
Make sure you follow #2309323: Allow #submit and #validate to be specified as methods of the form object, that will allow us to simplify the #submit's. and #validate's. Will probably conflict quite a bit with this, sorry in advance :)
This seems problematic, after deletion, it doesn't make sense to go back to the list, but we unfortunately currently don't have a link annotation for this :(
Maybe we can use the new standardized routes for this, assuming that they actually updated that one too? Checking.. Nope, they did not, obviously that would have made too much sense ;)
Instead of needing different forms, can we save the return value somewhere, something like $this->saveStatus = $this->entity->save() or something?
Comment #30
tstoecklerSo I recently saw #2022875: Resolve difference between submitForm(), submit(), and save() in EntityFormController and that it is making a lot of the same changes as here, which demotivated a bit to work on this issue any further... :-)
Let's see if that gets in anytime soon.
Comment #31
berdirThat got in :)
Re-roll is going to be painful I think...
Can give it a try, but as mentioned, looks like the last patch includes unrelated changes. Is that just a re-roll of the previous one?
Comment #32
berdirOk, worked on rerolling this, for save, there really isn't much left other than all the form subclasses, which I don't really like. But I do like the entity delete forms, so I extracted those and started with just those in #1728804: Introduce (Content)EntityDeleteForm and children to handle entity deletions.
I'd suggest to close this as a duplicate.
Comment #33
andypostMaybe better to separate save() and delete() as form methods here?
Comment #34
berdir@andypost: no idea what you meant.
Closing this as suggested, I think there is nothing left here not covered by those other issues.