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
[Form][Collection] Collection type is broken #1540
Comments
I've posted around for this issue so many times and it's been a mystery to everyone. I noticed the setter on collections calls the wrong one (if I can remember correctly, it calls the setter of whatever the Data Class is set to in the collection type instead of working with an owning side to allow persist to work correctly). Also, since there isn't an owning side, it's very hard to modify and remove elements. I currently have to grab all matched elements from the table, remove them, then add new ones on 1:M / M:1 collections. It's a pain in the ___. :( If this can be fixed, it would be a huge fix. |
@jstout24 it does not call any setter. It modify the collection directly. |
Note that the issue impacts the EntityType used with multiple set to true as it deals with a collection in this case. |
@stof yep, I understand. My current project has about 3 forms with a collection type utilizing multiple => true... The form handling is a pain. |
#1461 -- the right direction? =\ |
@jstout24 no, the issue is different. #1461 is about creating the child field. This one is about binding the submitted values. |
Gotcha, I barely looked at it before I headed to lunch. |
Related issue discussed here http://forum.symfony-project.org/viewtopic.php?f=23&t=35914 |
I agree adding add and remove methods would help manage bi-directional relationships here. |
+1 |
I'm glad this issue is being addressed :) +1 |
@stof: are you working on a patch? |
In the case of the deletion of a relation, the Form component clearly does the job by unsetting the object in the PersistentCollection object. @beberlei Could Doctrine2 propagates the changes? |
@marcw no, its the inverse side of the association. No Propagation by design. |
see #1745 |
@marcw did you find some time to work on another patch? May i help you? I'm also stuck with this issue because setters of the parent Entity are not being called. |
Well... I thought that it was better for me to sleep a bit on it in order to find a creative solution. I think that it would the easiest solution would be to implement snapshots in Doctrine's ArrayCollection. @beberlei Is that possible or is it just plain wrong? |
@marcw The change has not to be done in Doctrine but in the Form component. The only way to trigger the logic implemented by the user to update the owning side from the inversed side is to call the setters of the entity instead of modifying the collection. |
@stof As we already talk about this on IRC, I think you have a really good and deep understanding of this issue. Could you propose an implementation? |
@marcw The issue is that I don't know well enough how the binding works in the Form framework. I fear that this needs some very special handling that cannot be done as is. |
The furthest i could go is at the PropertyPathMapper class where there is a variable "isReference" that compares the form data with the ¿previous? data. But in case of that collection the previous data and the submitted one are the same (bizarre!) so the isReference var is always true. That leads to the $propertyPath->setValue never being called. Any suggestions or workarounds to have my many to many forms working? |
Do we have a solution there? http://sf.khepin.com/2011/08/inconsistent-behavior-of-symfony2s-form-framework/#comment-1172 |
This behaviour is intentional. When the form framework modifies related objects, it obtains the object (for example through getTags()) and the modifies it by reference without writing it back using setTags(). If you want to enforce the setter to be called, you need to set the option "by_reference" of the "tags" field to false (the default is true). Probably the default value of "by_reference" had better been false (to avoid these headaches), but this would be a non-BC change now. Automatically calling a addTag() and removeTag() in the case of collections would be nice, but difficult to implement. |
Has the form api been declared stable ? |
@keymaster: no |
Any update on this? |
Not that i know :( On Wed, Sep 14, 2011 at 08:56, Flukey <
|
I just came across this "behaviour" and its quite frustrating. If the form API hasn't been finalized, the fix for setting "by_reference" to false as the default is plausible unless there is an overall fix. |
I was talking to @stof today on IRC about this problem. I just wanted to add that even if you set In my case, This is a problem as I'm using this in a form that edits a pre-existing Entity that has sub-entities in a collection. Form replaces the old sub-entities in the collection with new ones using the data submitted, so this removes my old sub-entities and this isn't a valid logic. |
@Shreef yupp. i can confirm this behaviour and it pretty much ruins my hope of updating doctrine relations from the inverse side as i will never have any chance of knowing which entities were assigned before in my object. huuuge bummer. +1 for getting this fixed |
Commits ------- 9b0245b [Form] Made prefix of adder and remover method configurable. Adders and removers are not called if "by_reference" is disabled. 49d1464 [Form] Implemented MergeCollectionListener which calls addXxx() and removeXxx() in your model if found 7837f50 [Form] Added FormUtil::singularify() Discussion ---------- [Form] Forms now call addXxx() and removeXxx() in your model Bug fix: no Feature addition: yes Backwards compatibility break: no Symfony2 tests pass: yes Fixes the following tickets: #1540 Todo: adapt documentation ![Travis Build Status](https://secure.travis-ci.org/bschussek/symfony.png?branch=issue1540) Adds functionality for calling `addXxx` and `removeXxx` method in your model. All types returning a collection of values are affected: "collection", "choice" (with multiple selection) and "entity" (with multiple selection). Example: class Article { public function addTag($tag) { ... } public function removeTag($tag) { ... } public function getTags($tag) { ... } } And the controller: $form = $this->createFormBuilder($article) ->add('tags') ->getForm(); Upon modifying the form, addTag() and removeTag() are now called appropiately. --------------------------------------------------------------------------- by stof at 2012-02-01T18:23:49Z Really great --------------------------------------------------------------------------- by vicb at 2012-02-01T18:24:04Z Great !! Two suggestions: * make "add" and "remove" configurable, * introduce a base class for the remove listeners with (final?) `::getSubscribedEvents()` and `::getEventPriorities()` --------------------------------------------------------------------------- by haswalt at 2012-02-01T18:57:46Z +1 this --------------------------------------------------------------------------- by daFish at 2012-02-01T19:54:46Z +1 --------------------------------------------------------------------------- by michelsalib at 2012-02-01T20:55:37Z Can wait to have it! It will save lots of time trying to solve WTF effects and making workarounds. --------------------------------------------------------------------------- by bschussek at 2012-02-02T09:37:12Z @vicb: Your first point is done. The second, I don't understand. --------------------------------------------------------------------------- by stof at 2012-02-02T09:40:50Z @bschussek your branch conflicts with master according to github --------------------------------------------------------------------------- by vicb at 2012-02-02T09:52:40Z @bschussek my point is that I can stand hard-coded priorities which are error prone. A better solution might be to introduce constants (in `FormEvents` / `FormEventPriorities` ?) with meaningful names. --------------------------------------------------------------------------- by bschussek at 2012-02-02T10:21:52Z @stof Rebased @vicb I know, but who is responsible for managing priorities? There is no central entitty that can do this. (btw this is a general problem of the priority system of the EventDispatcher) @fabpot Ready to merge. --------------------------------------------------------------------------- by vicb at 2012-02-02T10:23:28Z @bschussek doesn't each form has is own dispatcher so there is no need for a global registry here, something local to the form could be good enough.
This is great news. Is it feasible to back propagate this to 2.0x? |
@keymaster New features are never backported. |
if it is possible technically to back port it (it may not be, and then there is nothing to talk about), this one might be worth doing. If we're saying it's not a bug, then the documentation for 2.0x needs to reflect a different way of processing forms from the inverse side. |
fixing the issue is part of the refactoring of the forms done for 2.1. I don't think it can be backported cleanly. |
Is there a recommended revision for testing the fix / form code? Or is head it? On Feb 5, 2012, at 2:40 PM, Christophe Coevoet reply@reply.github.com wrote:
|
it is the master branch. But take care that many bundles are not updated yet for the changes done in Form and Validator (including DoctrineMongoDBBundle and SonataAdminBundle for instance) |
Actually it should be possible to backport this by backporting MergeCollectionListener, MergeDoctrineCollectionListener and the changes in ResizeFormListener. AFAIK this should not depend on the changes in the choice lists. @stof: Do you have time to try this? |
Is the CollectionType working fine with entities on master branch? Little example with AcmePizzaBundle: https://github.com/RapotOR/AcmePizzaBundle/blob/master/Controller/OrderController.php#L111 It could be great to have unit tests for the CollectionType with entities in Doctrine bridge! |
@RapotOR @bschussek discovered a bug in the Doctrine ORM about cloning the persistent collections. This is the reason of the issue and should be fixed in Doctrine soon |
@stof Ok! Thanks for the light! |
to be exact, this bug probably affects other doctrine projects too as they basically copied the way the ORM did things |
@stof When I add a new item to the collection (allow_add activated), there is a issue :
The new item is an array and not OrderItem. (data_class is well defined). What is your opinion regarding unit tests in doctrine bridge for CollectionType with entities? |
Succeeded using:
|
Current version (62100f1), still behave BAD. If there is a way to set images directly by setImages(), please update the docs for collection type. |
setImages will be called if no addXXX nor removeXXX are present and by_reference is set to false :) |
This is sill bad, since I have no option to directly set the setter. My class method setImages() use addImage() as implementation of DRY idea. |
So...i'm afraid you'll have to rename your addImage to addManualImage or something |
This is just... awkward. Auto magic methods are cool, but user have to have control on them. To make SF user friendly you should allow to choose setter directly in options, not force them to give methods misleading names... |
guys, please open a new issue if you want to report an issue. We don't track comments on closed issues when we want to know what are the pending reports. |
I'm going to assume that I don't have to track the changes myself anymore as outlined in the doctrine persistence section for collection in 2.1 and master. public function addFoo($foo)
{
$this->foo[] = $foo;
$foo->setParent($this);
} But I'm really unable to figure out what's the removeFoo($foo) method supposed to look like public function addFoo($foo)
{
$foo->setParent(null);
$this->foo->removeElement($foo);
} Doesn't work as expected. Because the record in the DB stays just the relation column is set to null. Cascade remove option on the inverse side doesn't seem to do anything |
@mvrhov Removing a relation is not meant to remove the element in all cases. The OneToMany is about the relation, not about the related element. |
Thanks. This should really be in the documentation.. |
@mvrhov Would you mind opening a PR on symfony/symfony-docs fixing the documentation accordingly? |
The collection type is broken when using it for the inversed side of a Doctrine relation as it modifies the collection instead of calling some setter in the class. As Doctrine does not track changes in the inversed side, the changes are not persisted.
The best practice for Doctrine is to call the setter of the owning side in the setter of the inversed side. But as the setter is not called, this has no effect for the forms. And there is currently no way to change this behavior in user-land forms.
I suggested in #1141 to allow defining the setter used. A collection type should then call
addXXX
andremoveXXX
on the object instead of modifying the collection to allow having additionnal logic when adding and removing elements.The text was updated successfully, but these errors were encountered: