Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2 Doctrine2 Many To Many Form not Saving Entities

I am having some trouble with a many to many relationship. I have Users and Assets. I would like to be able to assign users to an asset on the asset page.

The code below displays a list of users when creating/editing an asset, however changes made to the user checkboxes do not save, while the rest of the data is persisted.

If I add an entry to users_assets through the mysql client, these changes are shown in the asset list.

User

class User extends BaseUser
{
    /**
     * @ORM\ManyToMany(targetEntity="Asset", inversedBy="users")
     */
    private $assets;
}

Asset

class Asset
{
    /**
     * @ORM\ManyToMany(targetEntity="User", mappedBy="assets")
     */
    private $users;
}

AssetType

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $form = $builder
        ->add('users', null, array(
            'expanded' => true,
            'multiple' => true
        ))
        ->getForm();

    return $form;
}
like image 451
Shawn Northrop Avatar asked Feb 01 '12 19:02

Shawn Northrop


3 Answers

For some reason I had to switch the doctrine mappings to get this to work:

Asset:
 /**
 * @ORM\ManyToMany(targetEntity="Adaptive\UserBundle\Entity\User", inversedBy="assets")
 * @ORM\JoinTable(name="user_assets")
 */
private $users;

User:
 /**
 * @ORM\ManyToMany(targetEntity="Splash\SiteBundle\Entity\Asset", mappedBy="users")
 */
private $assets;

Now when I save the asset it saves the users associated. I did not need to define builder->add as an entity or collection. I simply pass it null and it uses the mapping info to fill in the entity info:

AssetType:
->add('users', null, array('expanded' => "true", "multiple" => "true"))

Not exactly sure why I needed to have the inversedBy and JoinTable info on the Asset vs The User but it seems to be working now!

Thanks For The Suggestions!!!

like image 144
Shawn Northrop Avatar answered Nov 15 '22 19:11

Shawn Northrop


Weird enough I faced the same problem in 2016 and still had hard time finding the solution. I will share it for future googlers:

The problem is that what symfony essentially does when you save the form is this:

$asset->getUsers()->add($user)

And because you're on the inverse side of the relation it won't persist your changes.

What you really need is to make so that it calls this:

$asset->addUser($user)

Where addUser() is defined the following way on the Asset entity:

public function addUser(User $user) 
{
    //add to the inverse side
    $this->users->add($user);

    //add on the owning side (only this is persisted)
    $user->addAsset($this); //$user->assets->add($asset);
}

So in order to make symfony use that $asset->addUser() method, you should set

'by_reference' => false

on your users field for AssetType form.

More about this setting here http://symfony.com/doc/current/reference/forms/types/form.html#by-reference

Remember you also need to define removeUser() method in the same way (so that it removes entity from the owning relation)

like image 32
Pavel Dubinin Avatar answered Nov 15 '22 18:11

Pavel Dubinin


Not exactly sure why I needed to have the inversedBy and JoinTable info on the Asset vs The User but it seems to be working now!

The reason why your changes has been ignored is that doctrine persists only changes by the owning side of a relation (like @Florian said).

This is the link to Doctrine's documentation where this behaviour is explained: http://docs.doctrine-project.org/en/latest/reference/unitofwork-associations.html

like image 17
naitsirch Avatar answered Nov 15 '22 17:11

naitsirch