Doctrine 2 not persisting the relation from owning side one to Many - php

I have this code
// ONE to many Bidir -- inverse side
/**
* #ORM\OneToMany(targetEntity="Item", mappedBy="Room", cascade={"persist"})
**/
protected $items;
The other side
// ONE to many Bidir-- own side
/**
* #ORM\ManyToOne(targetEntity="Room", inversedBy="items")
* #ORM\JoinColumn(name="room_id", referencedColumnName="id")
**/
protected $room;
My Problem is that i go to item page and i select Room , then i can see items preselecetd in Room page
But if i go to Room page and i try to multiselect many items , then those are not persisted
EDIT: I have seen that it is only happening for OneToMany relation ship. For Manyto Many they are working fine
EDIT2:
I am talking about the backend area where i have the form and select box where i can select multiple items. This form/CRUD code / controllers are genrated by doctrine. SO i don't need to add any extra function. Any way this is my controller code
$editForm = $this->createForm(new RoomType(), $entity);
$request = $this->getRequest();
$editForm->bindRequest($request);
if ($editForm->isValid()) {
$em->persist($entity);
$em->flush();
When i try to walk through in controller like this
foreach($entity->getItems() as $item)
echo $item;
Then i can see all thye items there. So it means all items are there in main object but its not persisting them. i don't know why.
If there is owing , reverse side problem. How can i chnage the relationship from owing to inverse and inverse to owning

Your code is wrong, based on your comments in annotations.
This is the owning side, because you specify the inversedBy attribute:
/**
* ONE to many Bidir-- Inverse side
* #ORM\ManyToOne(targetEntity="Room", inversedBy="items")
* #ORM\JoinColumn(name="room_id", referencedColumnName="id")
**/
protected $room;
This is the inverse side (because it has the mappedBy attribute):
/**
* ONE to many Bidir -- owning side
* #ORM\OneToMany(targetEntity="Item", mappedBy="Room", cascade={"persist"})
**/
protected $items;
So your code says: Item is the owning side, Room is the inverse side.
The owning side of a bidirectional relationship must refer to its
inverse side by use of the inversedBy attribute of the OneToOne,
ManyToOne, or ManyToMany mapping declaration. The inversedBy attribute
designates the field in the entity that is the inverse side of the
relationship.
To make it more clean:
The owning side has to use the inversedBy attribute of the OneToOne,
ManyToOne, or ManyToMany mapping declaration. The inversedBy attribute
contains the name of the association-field on the inverse-side.
While, for the inverse side:
The inverse side of a bidirectional relationship must refer to its
owning side by use of the mappedBy attribute of the OneToOne,
OneToMany, or ManyToMany mapping declaration. The mappedBy attribute
designates the field in the entity that is the owner of the
relationship.
And, again:
The inverse side has to use the mappedBy attribute of the OneToOne,
OneToMany, or ManyToMany mapping declaration. The mappedBy attribute
contains the name of the association-field on the owning side.
Plus, another important consideration:
ManyToOne is always the owning side of a bidirectional assocation.
OneToMany is always the inverse side of a bidirectional assocation
Thus, for persisting items from the inverse side (room), you have to check which item has been selected/deselected and if room already contains that item or not.

Just use you loop to set room. Item is owing side and you editing inverse side (room).
Symfony2 never updates owing sides while editing inverse one.
The simplest solution for you is use loop before persisting room:
foreach($entity->getItems() as $item){
$item->setRoom($room);
}
and cascade on inverse side:
/**
* #ORM\OneToMany(targetEntity="Item", mappedBy="Room", cascade={"persist"})
**/
protected $items;
For my projects I made reusable solution based on collection events listeners.
If you have time & skills I encourage you to do the same

Related

Doctrine - Composite IDs

I am using symfony2 with the Doctrine entities and I have a problem with the next:
I know I could solve the problem putting an ID to the "club_has_torneo" and turning it into an entity, but to me creating an entity for that looks like something that should not be done. So I wanted to know if there is a way to solve this issue or if I have to do what I think I have to.
Thank you in advance.
I guess I'll submit my own two cents worth.
ORM stands for Object Relational Mapper. The basic idea is to figure out how to map your objects without worrying too much about the database schema. So you have three domain model entities: torneo, club and nadador. Great. Figure out how your application will use these entities. Don't worry about how the relations will end up being stored.
Once you have a working domain model then worry about persistence. The three domain entities clearly map to three doctrine entities. As far as the relations go, I personally am not a big fan of composite primary keys. I think they just complicate things while adding little value. So I would make Doctrine entities for your two has tables and just given them their own primary database id.
Note that these are Doctrine entities not domain entities. Your application code should never need to deal with these relational doctrine entities at all. So in my opinion
creating an entity for that looks like something that should not be
done
does not apply here. It is just a persistence detail.
I think the best solution is indeed to make a entity for your club_has_torneo table. This ClubHasTorneo entity has club_id and torneo_id as composite keys and holds the owning side of a many-to-many relationship between your ClubHasTorneo entity and Nadador entity. This relationship can be done with a join table using the 3 keys. Check the code below on how to do that.
Your database scheme will look exactly like you drew it.
Your ClubHasTorneo entity would look something like this:
<?php
namespace Application\Entity;
use Application\Entity\Club;
use Application\Entity\Torneo;
use Application\Entity\Nadador;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="club_has_torneo")
*/
class ClubHasTorneo
{
/** MANY-TO-ONE BIDIRECTIONAL, OWNING SIDE
* #var Club
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Application\Entity\Club", inversedBy="clubHasTorneos", cascade={"persist"})
* #ORM\JoinColumn(name="club_id", referencedColumnName="id")
*/
protected $club;
/** MANY-TO-ONE BIDIRECTIONAL, OWNING SIDE
* #var Torneo
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Application\Entity\Torneo", inversedBy="clubHasTorneos")
* #ORM\JoinColumn(name="torneo_id", referencedColumnName="id")
*/
protected $torneo;
/** MANY-TO-MANY BIDIRECTIONAL, OWNING SIDE
* #var Collection
* #ORM\ManyToMany(targetEntity="Application\Entity\Nadador", inversedBy="clubHasTorneos")
* #ORM\JoinTable(name="club_has_torneo_has_nadador",
* joinColumns={
* #ORM\JoinColumn(name="club_id", referencedColumnName="club_id"),
* #ORM\JoinColumn(name="torneo_id", referencedColumnName="torneo_id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="nadador_id", referencedColumnName="id")
* }
* )
*/
protected $natadors;
public function __construct()
{
$this->natadors = new ArrayCollection();
}
// setters and getters
}
my 5 cents
If you want your implementation to match the drawn table structure, then (in my opinion) you need create an entity out of the 'club_has_torneo' table (for 'club_has_torneo_has_matador' you don't need to).
The rationale being that if you try to achieve this without creating the entity, you would need to create the entity associations so, that the 'natador' table references the 'club' and 'torneo' directly - in which case the actual database relations wouldn't match with your drawn table relationship anymore (i.e. the natador wouldn't have relationship to the 'club_has_torneo' table).

Two attributes sharing the same OneToMany relationship to one entity Symfony2

Let's first describe my situation. I am using Symfony2 and I have a problem with a relationship between my entities.
I have two entities that are linked together. The two entities are AssociationQuestion and AssociationPossibleAnswer. I am currently creating a questionary software where one would have to link one possible answer on the left to another one possible answer on the right, such as in the following example:
Currently, I'm planning on having two attributes that are arrays in class AssociationQuestion that would hold many AssociationPossibleAnswer objects. The first array would contain the possible answers on the left and the second one would contain the possible answers on the right.
Therefore, to me, it looks like I would have two oneToMany relations in AssociationQuestion
AssociationQuestion:
oneToMany:
possibleAnswersLeft:
targetEntity: AssociationPossibleAnswer
mappedBy: associationQuestion
possibleAnswersRight:
targetEntity: AssociationPossibleAnswer
mappedBy: associationQuestion
Then, in AssociationPossibleAnswer, I would have one ManyToOne relation :
AssociationPossibleAnswer:
manyToOne:
associationQuestion:
targetEntity: AssociationQuestion
The problem is that I get the following error trying to validate my doctrine. It seems that you can't have two entities linked to one as I would wish to do...
* The field AssociationQuestion#possibleAnswersLeft is on the inverse side of a bi-directional relationship, but the specified mappedBy association on the target-entity AssociationPossibleAnswer#associationQuestion does not contain the required 'inversedBy=possibleAnswersLeft' attribute.
* The field AssociationQuestion#possibleAnswersRight is on the inverse side of a bi-directional relationship, but the specified mappedBy association on the target-entity AssociationPossibleAnswer#associationQuestion does not contain the required 'inversedBy=possibleAnswersRight' attribute.
I'm wondering if this is the proper way to set my relation between my two entities. Is it possible to have two attributes pointing to an entity while in return the entity does not know which attribute it is being pointed from.
This case cannot be solved with OneToMany associations.
You need 2 distinct relations between AssociationQuestion and AssociationPossibleAnswer. The way you've set it up, you only have 1 relation. Just look at the 1 ManyToOne association you created in AssociationPossibleAnswer.
And you're trying to have 2 opposite sides of that 1 relation, which is theoretically impossible. A relation can only have 2 endpoints (not 3).
Solution
Implement 2 (unidirectional) ManyToMany associations in AssociationQuestion, and make the foreign key pointing to AssociationPossibleAnswer unique:
class AssociationQuestion
{
/**
* #ORM\ManyToMany(targetEntity="AssociationPossibleAnswer")
* #ORM\JoinTable(name="association_question_association_possible_answer_left",
* joinColumns={#ORM\JoinColumn(name="association_question_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="association_possible_answer_id", referencedColumnName="id", unique=true)}
* )
*/
private $possibleAnswersLeft;
/**
* #ORM\ManyToMany(targetEntity="AssociationPossibleAnswer")
* #ORM\JoinTable(name="association_question_association_possible_answer_right",
* joinColumns={#ORM\JoinColumn(name="association_question_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="association_possible_answer_id", referencedColumnName="id", unique=true)}
* )
*/
private $possibleAnswersRight;
// ...
Doctrine calls this a One-To-Many, Unidirectional with Join Table association.

Multiple ManyToOne relationships in Doctrine

A simple issue, but I cant find any documentation for it.
I want to join one table to two other tables, on the same column with the same type of relationship. Pretty simple:
I have an Address table and a User table. Quite straight forward, one user can have many addresses:
User.php
/**
* #OneToMany(targetEntity="Address", mappedBy="user", cascade={"persist", "remove"})
*/
private $addresses;
Address.php
/**
* #ManyToOne(targetEntity="User", inversedBy="addresses")
*/
private $user;
Now I want to add a new table which will also use Address (One supplier may have many addresses also).
Supplier.php
/**
* #OneToMany(targetEntity="Address", mappedBy="**???**", cascade={"persist", "remove"})
*/
private $addresses;
Obviously I cant map by User, as that points from Address to User. I suppose I could add in another foreign key in the address table, but Im wondering if there is a better way to do this and continue using the same foreign key column for both User and Address.
You only need to specify a mappedBy attribute when there is a bidirectional relationship to determine which side is the owning side.
From the docs:
http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html#owning-side-and-inverse-side
A bidirectional relationship has both an owning side and an inverse side.
A unidirectional relationship only has an owning side.
The owning side of a relationship determines the updates to the relationship in the database.
...
The following rules apply to bidirectional associations:
The inverse side of a bidirectional relationship must refer to its
owning side by use of the mappedBy attribute of the OneToOne,
OneToMany, or ManyToMany mapping declaration. The mappedBy attribute
designates the field in the entity that is the owner of the
relationship.
In your case you only have unidirectional relationship which means you don't have to specify mappedBy, you may be able to leave it out.
Or you can add supplier to the Address class:
/**
* #ManyToOne(targetEntity="Supplier", inversedBy="addresses")
*/
private $supplier;
which then allows you to set mappedBy="supplier" in Supplier.php
/**
* #OneToMany(targetEntity="Address", mappedBy="supplier", cascade={"persist", "remove"})
*/
private $addresses;

How to use the cascade option in Doctrine2 to have associated entities automatically persisted?

Can someone explain me this:
$user = new User();
/* why do I have to call Entity Comment while trying to insert into db? */
$myFirstComment = new Comment();
$user->addComment($myFirstComment);
$em->persist($user);
$em->persist($myFirstComment);
$em->flush();
Why do I have to call Entity Comment while trying to insert into db?
I have cascade for that.
Does this mean that if I have 50 relation in User Entity with other
entities I have to call each relation manually when trying to
update/insert/delete?
Why does cascade exist if I have to do all manually?
If I have to call all that relation manually it is kind of stupid to use Doctrine at all.
I do not get this. Any help is appreciated.
This is connected with this: doctrine 2, unable to insert to database when relation is present
To have Doctrine automatically handle the persistence of your User#comments property you have to set cascade to the "persist" operation.
The cascade ( persist, remove , merge, all ) option gives you the ability to ommit ...
$em->persist($myFirstComment);
... if you set it correctly on your inverse side of a bidirectional relation for example.
It can also automatically remove User#comments if you remove a User entity with cascade "remove" !
example:
/**
* Bidirectional - One-To-Many (INVERSE SIDE)
*
* #OneToMany(targetEntity="Comment", mappedBy="author", cascade={"persist", "remove"})
*/
private $comments;
Read more on Association mapping and cascade in the Transistive Persistence / Cascade Options chapter of the documentation.
Please remember:
Doctrine will only check the owning side of an association for changes.
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)
OneToMany associations are never the owning side.
The inverse side has to use the mappedBy attribute of the OneToOne, OneToMany, or ManyToMany mapping declaration. The mappedBy attribute contains the name of the association-field on the owning side
The owning side has to use the inversedBy attribute of the OneToOne, ManyToOne, or ManyToMany mapping declaration. The inversedBy attribute contains the name of the association-field on the inverse-side.
ManyToOne is always the owning side of a bidirectional association.
OneToMany is always the inverse side of a bidirectional association.
Furthermore:
you only have to call persist if you create a new root entity ( i.e. $user = new User() ) which is not already managed by doctrine ( and you don't have to call persist on $myFirstComment in your example if you have set the cascade option correctly ).
Otherwise you only have to call flush if the entity hasn't for some reason been detached.

Doctrine - OneToOne Unidirectional vs OneToOne Bidirectional

I joust started playing around with Doctrine ORM library, and Im learning about all associations between tables.
So Im stuck with differences in Unidirectional and Bidirectional relation.
As I get it, Unidirectional relation has primary key only on one side, and that side is owning side right?
And Bidirectional relation have primary key in both tables and therefore you can have relation from both sides, and set constrains on both sides.
Now, Im reading through Doctrine documentation about relations and there you have:
Unidirectional and Bidirectional associations.
But they produce the same SQL, and the same tables with the same primary key-s and constrains. So I dont really see any difference in those two. And both examples have primary key on one side.
As I get it the true Bidirectional relation should have primary keys in both tables pointing to the other table right? And with given example on Doctrine documentation that is not the case. Both examples give the same result and are the same.
So what I did, is this, lets say I have User and Card Entity, and want relation to be OneToOne Bidirectional.
/**
* #Entity
* #Table(name="users")
*/
class User
{
/**
* #Id
* #GeneratedValue
* #Column(type="bigint")
*/
protected $id;
/**
* #OneToOne(targetEntity="Card", mappedBy="User")
* #JoinColumn(name="card_id", referencedColumnName="id")
*/
protected $card;
/**
* #Column(name="user_name", type="string")
*/
protected $userName;
/**
* #Column(name="user_pass", type="string")
*/
protected $userPass;
}
/**
* #Entity
* #Table(name="cards")
*/
class Card
{
/**
* #Id
* #GeneratedValue
* #Column(type="bigint")
*/
protected $id;
/**
* #OneToOne(targetEntity="User", inversedBy="Card")
* #JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* #Column(name="post_title", type="string")
*/
protected $cardType;
}
The difference here is I wrote #JoinColumn in both objects/entities. And in Doctrine example there is only one.
Now I would get what I think is Bidirectional relation. If i look at EER diagram, I can see one line pointing from user to card, and the other from card to user.
So basicly did I get this right?
Is the Doctrine documentation wrong? :D
How would Bidirectional OneToOne relation look in EER diagram?
Thanks!
The only difference is in the PHP class interface, i.e. in the presence or absence of the property that points back to the owner (e.g. the $customer property in the mentioned Doctrine example). In other words Doctrine just needs to know whether it should take care about a single property ($shipping) or two properties ($cart and $customer). There is no other difference. Therefore, the SQL code is the same (because one foreign key is sufficient for representing any 1:N relationship) and there would no difference in EER diagram neither (because in EER you typically do not solve such PHP-related implementation details).
Unidirectional and bidirectional have nothing to do with the background algorithm how to create these connections in the database layer.
All they talk about is how the connections can be used. In an unidirectional relationship you can access the target only from one site. An bidirectional relationship allows the connection to be called from two (both) sides.
So in an unidir. rel. model_a can get to model_b, but model_b cant get to model_a (without extra work).
If you now use a bidir. rel both models can access each other without problems
In doctrine terms, a unidirectional relationship defines a
$modelA->getModelB() method, but not a $modelB->getModelA() method, whereas a bidirectional relationship defines both methods (or accessors, however you want to call them)
in an uml diagram it would look like this:
unidirectional
modelA --X------> modelB
bidirectional
modelA <--------> modelB

Categories