Symfony form and doctrine inverse side association
Dealing with doctrine associations is getting more and more simple with new versions. But doctrine is still based on few concepts which need to be understood. Owning and reverse side is one of them ; we will see how it can be handled when working with symfony2 forms.
Imagine the situation where you have a User which can have multiples addresses. You will create 2 entities like this :
Note: You can run ./app/console doctrine:schema:validate to make sure the schema is correct.
As the user can have multiples addresses, Address entity will be the owning side and User the inverse side.
Now imagine you want to create a new User using Symfony2 form. The form will looks like :
Note: AddressType() is the form for Address containing all informations needed (address, city etc.)
Lets do the controller now as usually :
After trying to persist the $user entity (last line), we are getting "user_id cannot be null" error.
This is due to the owning and inverse side doctrine concept. Doctrine will only check the owning side of the associations and we are working with the inverse side http://docs.doctrine-project.org/en/latest/reference/unitofwork-associations.html:
Doctrine needs to know which of these two in-memory references is the one that should be persisted and which not.(...)
Changes made only to the inverse side of an association are ignored. Make sure to update both sides of a bidirectional association (or at least the owning side, from Doctrine’s point of view).
The owning side of a bidirectional association is the side Doctrine “looks at” when determining the state of the association, and consequently whether there is anything to do to update the association in the database.
Note: for OneToOne association, its depends where the foreign key is stored.
As we cannot change the association direction, the only solution is to manually set
the association.
We can do it by 2 ways :
- updating User.php setAddress method.
- adding addAddress/removeAddress methods in User.php
Solution 1. Update setter in User.php
The first solution is to update, as we can do it for OneToOne association, the setter in User changing the setter from :
to :
This way, the User is directly set when calling :
Note: this solution is working for OneToOne association too.
Solution 2. Add addXXX/removeXXX methods.
The second solution is to add 2 methods in the User.php entity which will be called automatically by Symfony (https://github.com/symfony/PropertyAccess/blob/master/PropertyAccessor.php#L323) when adding or removing an Address.
http://docs.doctrine-project.org/en/latest/reference/working-with-associations.html
If methods are not called, make sure to set the option by_reference to false.
Note : setting the inversed side of the association is bad for performance. You should do it carefully. http://docs.doctrine-project.org/en/latest/reference/improving-performance.html.