I have a problem with my ManyToMany relation in doctrine2. The relation doesn't persist even though the relation exists. If i check afther the persist in two foreach loops the correct objects are returned.
The first class is Document.
class Document extends BaseEntity
{
....
/**
* #ORM\ManyToMany(targetEntity="Job", mappedBy="documents", cascade={"all"})
* #ORM\JoinTable(name="job_document")
*/
protected $jobs;
....
The second class is Job
class Job extends BaseEntity
{
....
/**
* #ORM\ManyToMany(targetEntity="Document", inversedBy="jobs", cascade={"all"})
* #ORM\JoinTable(name="job_document")
*/
protected $documents;
....
In my controller I do the following:
$job->addDocument($document);
$document->addJob($job);
$em->persist($job);
$em->flush();
The add functions work fine. I can see it when I loop through the objects afther I do this.
It seems to me that you only try to update the inverse side and not the owning side of the relationship.
As pointed out in the doctrine documentation:
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)
Related
I'm currently building Entity model and one of my Doctrine Entities have ManyToMany relation with an external dictionary (like ENUM). So the entity field will be an Array of Enum.
I'm looking for a way to have it as an array field on my entity, but to store it as a separate DB table.
Would like to get any advice/links/etc.
The question is a bit out of context but..
A many to many is already an array (Iterator) in your entity.
You can create your own entity acting as a Many To Many and set the column as enum.
Finally, I've decided to create an Entity to store this relation. To make sure it will be deleted on unlinking from the parent entity, I've used the orphanRemoval=true option on the OneToMany relation side.
class Entity {
/**
* #ORM\OneToMany(targetEntity="EntityType", mappedBy="entity", orphanRemoval=true)
*/
protected $types;
}
class EntityType {
/**
* #ORM\ManyToOne(targetEntity="Entity")
*/
protected $entity;
/**
* #ORM\Column(type="MyEnum")
*/
protected MyEnum $type;
}
I have an issue with the doctrine relationship. I try different ways but anything won't work.
Idea is that I have a News entity and every news should have many comments. So I try next:
The News entity:
/**
* #ORM\OneToMany(targetEntity="App\ORM\Entity\NewsComment", mappedBy="news")
*/
protected \Doctrine\Common\Collections\Collection $comments;
/**
* News constructor.
*/
public function __construct() {
$this->comments = new ArrayCollection();
}
And NewsComment entity:
/**
* #ORM\ManyToOne(targetEntity="App\ORM\Entity\News", inversedBy="comments")
*/
protected \App\ORM\Entity\News $news;
Every entity has its own get and set methods as well.
But, when I receive a News entity a can get comments collection but it always empty. On the other hand, I can take any NewsComment entity and get from this News entity. It is working fine. But not to another way.
Is anything wrong with my code?
Doctrine sets owned (non-inversed) collection as lazy by default.
When retrieving an entity by database, you should see an empty PersistentCollection instead of ArrayCollection, with initialized property set to false.
When calling any method on that collection, doctrine fires the queries needed to initialize the collection and populate it.
Collection emptiness should be only checked invoking isEmpty.
Let's assume i have an one-to-one relation with one entity person
class Person
{
...
/**
* #var Player
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Player", inversedBy="person")
*/
private $player;
...
}
and one entity Player
class Player
{
...
/**
* #var Person
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Person", mappedBy="player")
*/
private $person;
...
}
Now the person side is holding the foreign key for the person.
Every try to access something from the inversed side is failing, for example
$em->getRepository('AppBundle:Player')->findByPerson();
ends up in
[Doctrine\ORM\ORMException]
You cannot search for the association field
'AppBundle\Entity\Player#person', because it is the inverse side of
an association. Find methods only work on owning side associations.
Doing the same to the owning side (find player for the person), everything is fine.
I cant figure out: How can i access entities from both sides?
I need that, because i need to know, which player hasn't already persons assigned and vice versa. I thought, doctrine is loading the related entities ... for this case plain sql seems the easier solution for that? Or have i really to deal with dql and joins?
In your class Player, can you try with this :
class Player
{
...
/**
* #var Person
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Person", mappedBy="player")
* #ORM\JoinColumn(name="person_id", referencedColumnName="id")
*/
private $person;
...
}
Here is the doctrine doc: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-one-bidirectional
On this page of association mapping, there's an example in the manytomany section. But I don't understand which entity (group or user) is the owning side.
http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html#many-to-many-bidirectional
I've put the code here too
<?php
/** #Entity */
class User
{
// ...
/**
* #ManyToMany(targetEntity="Group", inversedBy="users")
* #JoinTable(name="users_groups")
*/
private $groups;
public function __construct() {
$this->groups = new \Doctrine\Common\Collections\ArrayCollection();
}
// ...
}
/** #Entity */
class Group
{
// ...
/**
* #ManyToMany(targetEntity="User", mappedBy="groups")
*/
private $users;
public function __construct() {
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
// ...
}
Do I read this annotation like this:
User is mappedBy groups so group is the entity that does the connection management, thus the owning side?
Also, I've read this in the docs:
For ManyToMany bidirectional relationships either side may be the owning side (the side that defines the #JoinTable and/or does not make use of the mappedBy attribute, thus using a default join table).
This lets me think that User would be the owning side as the JoinTable annotation is defined in that entity.
But I don't understand which entity (group or user) is the owning side
The User entity is the owner. You have the relation of groups in User:
/**
* #ManyToMany(targetEntity="Group", inversedBy="users")
* #JoinTable(name="users_groups")
*/
private $groups;
Look above, $groups var contains the all groups associated to this user, but If you notice the property definition, $groups var has the same name of mappedBy value (mappedBy="groups"), as you did:
/**
* #ManyToMany(targetEntity="User", mappedBy="groups")
*/
private $users;
What does mappedBy mean?
This option specifies the property name on the targetEntity that is the owning side of this relation.
Taken from the docs:
In a one-to-one relation the entity holding the foreign key of the
related entity on its own database table is always the owning side of
the relation.
In a many-to-one relation the Many-side is the owning side by default,
because it holds the foreign key. The OneToMany side of a relation is
inverse by default, since the foreign key is saved on the Many side. A
OneToMany relation can only be the owning side, if its implemented
using a ManyToMany relation with join table and restricting the one
side to allow only UNIQUE values per database constraint.
Now, I understand ManyToMany can be confusing some times.
For Many-To-Many associations you can choose which entity is the owning and which the inverse side. There is a very simple semantic rule to decide which side is more suitable to be the owning side from a developers perspective. You only have to ask yourself, which entity is responsible for the connection management and pick that as the owning side.
Take an example of two entities Article and Tag. Whenever you want to connect an Article to a Tag and vice-versa, it is mostly the Article that is responsible for this relation. Whenever you add a new article, you want to connect it with existing or new tags. Your createArticle form will probably support this notion and allow to specify the tags directly. This is why you should pick the Article as owning side, as it makes the code more understandable:
<?php
class Article
{
private $tags;
public function addTag(Tag $tag)
{
$tag->addArticle($this); // synchronously updating inverse side
$this->tags[] = $tag;
}
}
class Tag
{
private $articles;
public function addArticle(Article $article)
{
$this->articles[] = $article;
}
}
This allows to group the tag adding on the Article side of the association:
<?php
$article = new Article();
$article->addTag($tagA);
$article->addTag($tagB);
So, in short, whatever makes more sense to you. You choose the owning and the inverse side of the relationship. :)
Source: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html
as pointed out here: Doctrine 2.1 - Map entity to multiple tables Doctrine2 does not allow mapping of one object to multiple tables.
I currently have a Mysql db setup similar to this:
base_entity: id, some_basic_data_columns
state: id, state, entity_id (FK to base_entity.id), start_time, end_time, ...
entity_one: id (FK to base_entity.id), some_specific_data
entity_two: id (FK to base_entity.id), some_specific_data
and so on...
In a way, entity_x is "extending" base_entity, and all these entities can have multiple states. To have proper foreign keys I would have to either have separate state tables (which I don't want to do because they will structurally be the same ), or do it like this.
The base entity by itself is useless, id could even be boiled down to just the id field to allow to join with each child entity to multiple states.
I do not need a BaseEntity class, but I do need for each child Entity to have a getStates() method. Of course I may actually have an abstract entity class, but concrete entities will extend it, not have it as a property like they would if I would map them as one would map other one-to-one relationships
Since Doctrine will not allow me to map EntityOne to both entity_one and base_entity table I have to ask:
Is this bad design? Am I overlooking some other way to solve this elegantly? I know other DMBSs have inheritance, but for instance PostgreSql would still not allow me to join the base_entity to state if no physical base_entity exists for a child.
I could do something like this on the code side:
class EntityOne {
// baseEntity as a property
private $_baseEntity;
// private getter for the base table
private getBaseEntity();
// and getters like this for properties in the base table
public getStates(){
return $this->getBaseEntity()->getStates();
}
}
This way the entity would behave like a single entity (not combined from base and child) to the outside world, but it would still require that I write a separate BaseEntity class and all the config info to connect it to other entity classes
Basically, what I'm asking is: is this a Db design issue, and I got it completely wrong from the start (and if I did, which is the "best" approach), or is this a code issue, and I should work around it with code (if so, is my approach in 2. ok, or are there better ways to deal with this), and are there ORMs which allow for multiple table mapping?
Many thanks in advance.
You could use Class Table Inheritance (see Doctrine documentation about that), defining a BaseEntity entity class, and and create EntityOne and EntityTwo extending that.
You could define the relationship between the BaseEntity class and the State entity class as one-to-many association - if I understood right what you wanted, providing the needed getState() method in the BaseEntity class.
Something like this:
/**
* #Entity
* #Table(name="base_entity")
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="entity_type", type="string")
* #DiscriminatorMap({"entity_one"="EntityOne", "entity_two"="EntityTwo"})
*/
class BaseEntity {
/**
* #Id
* #Column(type="integer")
*/
protected $id;
/**
* #OneToMany(targetEntity="State", mappedBy="entity)
**/
protected $states;
public function getStates() {
return $this->states;
}
...
}
/**
* #Entity
* #Table(name="entity_one")
*/
class EntityOne extends BaseEntity {
...
}
/**
* #Entity
* #Table(name="entity_two")
*/
class EntityTwo extends BaseEntity {
...
}
/**
* #Entity
* #Table(name="state")
*/
class State {
/**
* #ManyToOne(targetEntity="BaseEntity", inversedBy="states")
* #JoinColum(name="entity_id", referencedColumnName="id")
*/
protected $entity;
public function getEntity() {
return $this->entity;
}
...
}