Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Form] CollectionType apply prototypeOptions to ResizeFormListener new fields #49835

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

Merged
merged 1 commit into from
Mar 31, 2023
Merged

[Form] CollectionType apply prototypeOptions to ResizeFormListener new fields #49835

merged 1 commit into from
Mar 31, 2023

Conversation

Thorry84
Copy link
Contributor

Fixes issue 49786

Q A
Branch? 6.2
Bug fix? yes
New feature? no
Deprecations? no
Tickets Fix #49786
License MIT

This pull request provides a fix for issue 49786.

By passing the prototypeOptions to the ResizeFormListener from the CollectionType and applying it to new fields it makes the prototypeOptions function as intended. Otherwise the use case such as in this blog aren't possible: https://symfony.com/blog/new-in-symfony-6-1-customizable-collection-prototypes

I've updated the existing ResizeFormListener tests to handle the new signature and added a new test to the CollectionType which would fail without this fix and pass with the fix.

@Thorry84 Thorry84 requested review from xabbuh and yceruto as code owners March 27, 2023 16:00
@carsonbot carsonbot added this to the 6.2 milestone Mar 27, 2023
@carsonbot carsonbot changed the title CollectionType apply prototypeOptions to ResizeFormListener new fields [Form] CollectionType apply prototypeOptions to ResizeFormListener new fields Mar 30, 2023
@Thorry84
Copy link
Contributor Author

Thorry84 commented Mar 31, 2023

Yeah good point. Fixed!

$this->deleteEmpty = \is_bool($deleteEmpty) ? $deleteEmpty : $deleteEmpty(...);
$this->prototypeOptions = $prototypeOptions;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$this->prototypeOptions = array_replace($options, $prototypeOptions); (and remove the array_replace below)

Copy link
Contributor Author

@Thorry84 Thorry84 Mar 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm that seems not right?

I feel like the way the CollectionType gets its prototype options is by doing the array merge on the entry options (line 30 in the CollectionType). But that isn't by definition the way the ResizeFormListener should do it as well?
I'm not sure how it was decided for the CollectionType to handle the prototype options this way. I feel like it would be just as valid to have the prototype options be completely independent of the entry options.

I think it's also important to have the way the options are used in the CollectionType and the way it's passed to the ResizeFormListener be the same. Maybe I should have prevented the double merge? That way if it is decided that behavior is to change, it's always the same for both the CollectionType and the ResizeFormListener.

What do you think?

Thanks so much for the feedback :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then we should fallback to $options when no prototype options are given, to preserve the current behavior:

$this->prototypeOptions = $prototypeOptions ?? $options;

and change the argument to array $prototypeOptions = null

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes totally correct. I've changed the default value for the prototypeOptions and fallback to options.
I've also changed the CollectionType constructor to prevent the double merge.

Thanks so much for your help, I'm not very good at this.

@nicolas-grekas
Copy link
Member

Thank you @Thorry84.

@nicolas-grekas nicolas-grekas merged commit 6b9538b into symfony:6.2 Mar 31, 2023
@fabpot fabpot mentioned this pull request Mar 31, 2023
@AlexeyKosov
Copy link

@Thorry84

Not sure how exactly, but this fix has crashed some functionality on my sites.
It now throws a Entity has to be managed or scheduled for removal for single computation error since entities loaded in the forms are no longer managed by ORM's Unit of Work.

It's not a bug report, just wanted to let you know it's a possible BC though.

@Thorry84
Copy link
Contributor Author

Thorry84 commented Apr 3, 2023

Hmmm that's not good. I'm not sure how my changes could be a BC break.

However the previous implementation was inherently broken. As I pointed out the example given in the blog post was simply not possible. So in the past the functionality not working might have masked any issues with unmanaged entities.

As far as I can see I don't know of any way the ResizeFormListener could cause Entities to become unmanaged. I would suspect an issue with the wrong transformers being applied to the form fields?

If you have any code to share I would love to help you investigate the issue.

@AlexeyKosov
Copy link

AlexeyKosov commented Apr 4, 2023

@Thorry84

It must have been because I used an unmanaged empty Doctrine entity as prototype data

        $builder->add('items', CollectionType::class, [
            'entry_type' => ItemType::class,
            'allow_add' => true,
            'by_reference' => true,
            'label' => false,
            'prototype' => true,
            'prototype_data' => new ItemEntity(),
        ]);

In 6.2.7, the empty detached entity was used only for prototyping. The ResizeFormListener's options looked like

array:1 [▼
  "block_name" => "entry"
]

But now, in 6.2.8, the $prototypeOptions used instead of $options, looks this way:

array:4 [▼
  "required" => true
  "label" => "__name__label__"
  "block_name" => "entry"
  "data" => App\Entity\ItemEntity
]

So the blank detached entity comes into play instead of being discarded as before.

In other words - does it make sense to use prototype data for populating new collection elements rather than only for prototyping? And even if so, it has not been used until 6.2.8, which seems to be an explicit BC.

@Thorry84
Copy link
Contributor Author

Thorry84 commented Apr 4, 2023

That explains the error you've been getting.

In my opinion the previous behavior where all the options were discarded was incorrect. So passing along the options to the new fields is a bug fix and not a BC break.

However the prototype_data field is maybe an exception to this rule. Would someone expect new fields to be populated with data?

Discussions about this in the past I've been able to find point out the data is for initialization only and not on submit. However in the past when passing data to options this would be applied to new fields, which is incorrect in my opinion. Applying the prototype_data would make more sense than applying data.

So I'm not really sure if this would be considered a BC break or a bugfix? Either case sounds valid to me.
My 2 cents is that this is a bugfix.


In your code, why isn't the new data handled? If there are new entries in the form, there are new entities to be persisted?

@AlexeyKosov
Copy link

In your code, why isn't the new data handled? If there are new entries in the form, there are new entities to be persisted?

The actual entities are loaded by ID from a repository first in a preSubmit hook, and only then updated.
So there's no need to persist them, since all the entities are existing, not new. I.e. the collection is used for updating records, not adding.

In my opinion the previous behavior where all the options were discarded was incorrect. So passing along the options to the new fields is a bug fix and not a BC break.

Ok, no problem, I just wanted you to be aware of that possible issue.
Anyway, thank you for adding new features!

@Thorry84
Copy link
Contributor Author

Thorry84 commented Apr 4, 2023

Thanks for letting me know. Happy to help :)

@Thorry84
Copy link
Contributor Author

Thorry84 commented Jun 2, 2023

If somebody comes across this issue from Google: It doesn't work for the use case stated in the blog and my fix didn't solve the issue fully for that use case.

The problem is, the prototype options are used for new fields, but only when creating new fields in the UI. So when you have a form, you can disable fields for existing data and have new fields available. This part now works with my fix, but whenever there is a validation error in the form, the submitted data is seen as "old" data and the fields are now disabled. This means the user can't even fix the validation errors and is in a sense soft-locked.

So even though the fix did technically fix the functionality, the supposed use-case simply isn't possible using only this feature. YMMV

Tagging @javiereguiluz to maybe edit the blog post to point this out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants