Enter entity property from the inversed side in one to one relation - php

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

Related

Doctrine in Symfony: use a single “Author” associative entity related to different entities

I'm developing a custom content management system with Symfony 5 and Doctrine.
I'm trying to implement a relation between the entities Document and Video (actually there are many more, but for simplicity sake let's say are just two) and the User entity.
The relation represent the User who wrote the document or recorded the video. So the relation here is called Author. Each document or video can have one or more author. Each User can have none or more document or video.
I would like to use just a single associative Author associative entity, like this:
entity_id|author_id|entity
Where:
entity_id: is the id of the document or video
author_id: is the user_id who authored the entity
entity: is a constant like document or video to know to which entity the relation refer to
The problem is that I cannot understand how to build this in Doctrine. Was this a classic SingleEntity<-->Author<-->Users relationship I would have build it as a ManyToMany item, but here it's different.
Author would probably contain two ManyToOne relations (one with the User entity and one with either the Document or the Video entity) plus the entity type field, but I really don't know how to code the "DocumentorVideo`" part. I mean:
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=??????????, inversedBy="authors")
* #ORM\JoinColumn(nullable=false)
*/
private $entity; // Document or Video
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="articles")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\Column(type="smallint")
*/
private $entityType;
How should I manage the first field?
Don't know if would be better to store it under two differents attributes. If not and mandatory, I think those "objects" should have a common interface or something, so take a look to doctrine inheritance that should fulfill your needs
My suggestion is to store the entity namespace Ex. Acme\Entity\Document in a property and the id in another and to use the entity manager to get the entity.
Edit: Though you won't have the relation, I prefer that way over others because it is reusable and the performance is rather the same. Also if I need to pass it to a JSON response, I just create a normalizer and I am good to go.
/**
* #ORM\Column(type="string")
*/
private $entityNamespace;
/**
* #ORM\Column(type="integer")
*/
private $entityId;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function getEntity()
{
return $this->em->getRepository($this->entityNamespace)->find($this->entityId);
}

Persisting entity with Doctrine association

I'm having trouble trying to persist an entity with an association using Doctrine.
Here's the mapping on my owning side: (User.php)
/** #Role_id #Column(type="integer") nullable=false */
private $role_id;
/**
* #ManyToOne(targetEntity="Roles\Entities\Role")
* #JoinColumn(name="role_id", referencedColumnName="id")
*/
private $role;
There's no mapping on the inverse side, I tried with (OneToMany) and it didn't seem to make a difference.
Basically, I'm passing a default value of 2 (integer) to a method setRole_id but it shows up as blank when I actually go to persist the entity which causes a MySQL error as that column doesn't allow nulls.
Edit 1:
Literally just persisting this for role_id
$this->user->setRole_id( 2 );
Cheers,
Ewan
Your mapping seems incorrect. Try to rewrite it as follows:
/**
* #ManyToOne(targetEntity="Roles\Entities\Role")
* #JoinColumn(name="role_id", referencedColumnName="id", nullable=false)
*/
private $role;
In other words, you only need to describe the role_id as the join column of your relationship. You don't need to map it as a "normal" column. Then just write and use a regular setter declared like the one below:
public function setRole(Roles\Entities\Role $role) {
$this->role = $role;
}
Use the above instead of $this->user->setRole_id(2) and persist your user entity. Doctrine should automatically take care of storing the correct entity ID in the foreign key field for you.

doctrine2 checking if one entity is related to other over ManyToMany relation

For example I have 2 entities: event and user both has ManyToMany relation.
class Event {
/**
* #Id #Column(type="integer") #GeneratedValue
*/
protected $id;
/**
* #ManyToMany(targetEntity="User", inversedBy="participated_events")
*/
protected $participations;
}
and
class User {
/**
* #Id #Column(type="integer") #GeneratedValue
* */
protected $id;
/**
* #ManyToMany(targetEntity="Event", inversedBy="participations")
*/
protected $participated_events;
}
how can i check if $em->getRepository('event')->find(RANDOM_ID) and $em->getRepository('user')->find(RANDOM_ID) is related to each other over participation? In other words how to check if user is participated in this event? What is the best way? I know that i could get all participated events and check over foreach() but i think that it must be an easier way.
Thanks.
You have to create a custom repository DQL and do select fields you want to do.;
I, personally, avoid Doctrines many to many relations as it is easier for me to do things you want to do, aslo if i need extra columns in join_table. So, I almost always create a join Entity with corresponding manyTo one and onetoMany references. So in your instance there would be a ParticipatedEvent entity which has manyToOne users and manyToOne Events (and on their target Entites oneToMany) You stil have your relationship but you have extra join Model (or Entity)
So, you end up like this:
$particpated_event = $em->getRepository('ParticipatedEvents')->findOneBy('event_id'=>RANDOM_ID, 'user_id'=>RANDOM_USER_ID);
if($particpated_event) {
$event = $particpated_event->getEvent(); //or get user.
}
and you can stil do things like this
$particpated_events = $em->getRepository('event')->find(RANDOM_ID)->getParticipatedEvents();
foreach( $particpated_events as $particpated_event){
$user = $particpated_event->getUser();
}
Again see what best suits fro you. You can, as said, create Repository class with DQL query

Doctrine2 ManyToMany-relation doesn't save

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)

What is the difference between inversedBy and mappedBy?

I am developing my application using Zend Framework 2 and Doctrine 2.
While writting annotations, I am unable to understand the difference between mappedBy and inversedBy.
When should I use mappedBy?
When should I use inversedBy?
When should I use neither?
Here is an example:
/**
*
* #ORM\OneToOne(targetEntity="\custMod\Entity\Person", mappedBy="customer")
* #ORM\JoinColumn(name="personID", referencedColumnName="id")
*/
protected $person;
/**
*
* #ORM\OneToOne(targetEntity="\Auth\Entity\User")
* #ORM\JoinColumn(name="userID", referencedColumnName="id")
*/
protected $user;
/**
*
* #ORM\ManyToOne (targetEntity="\custMod\Entity\Company", inversedBy="customer")
* #ORM\JoinColumn (name="companyID", referencedColumnName="id")
*/
protected $company;
I did a quick search and found the following, but I am still confused:
example 1
example 2
example 3
mappedBy has to be specified on the inversed side of a (bidirectional) association
inversedBy has to be specified on the owning side of a (bidirectional) association
from doctrine documentation:
ManyToOne is always the owning side of a bidirectional assocation.
OneToMany is always the inverse side of a bidirectional assocation.
The owning side of a OneToOne assocation is the entity with the table containing the foreign key.
See https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/unitofwork-associations.html
The answers above were not sufficient for me to understand what was going on, so after delving into it more I think I have a way of explaining it that will make sense for people who struggled like I did to understand.
inversedBy and mappedBy are used by the INTERNAL DOCTRINE engine to reduce the number of SQL queries it has to do to get the information you need. To be clear if you don't add inversedBy or mappedBy your code will still work but will not be optimized.
So for example, look at the classes below:
class Task
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="task", type="string", length=255)
*/
private $task;
/**
* #var \DateTime
*
* #ORM\Column(name="dueDate", type="datetime")
*/
private $dueDate;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="tasks", cascade={"persist"})
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
}
class Category
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="Task", mappedBy="category")
*/
protected $tasks;
}
These classes if you were to run the command to generate the schema (for example, bin/console doctrine:schema:update --force --dump-sql) you will notice that the Category table does not have a column on it for tasks. (this is because it does not have a column annotation on it)
The important thing to understand here is that the variable tasks is only there so the internal doctrine engine can use the reference above it which says its mappedBy Category. Now... don't be confused here like I was... Category is NOT referring TO THE CLASS NAME, its referring to the property on the Task class called 'protected $category'.
Like wise, on the Tasks class the property $category mentions it is inversedBy="tasks", notice this is plural, this is NOT THE PLURAL OF THE CLASS NAME, but just because the property is called 'protected $tasks' in the Category class.
Once you understand this it becomes very easy to understand what inversedBy and mappedBy are doing and how to use them in this situation.
The side that is referencing the foreign key like 'tasks' in my example always gets the inversedBy attribute because it needs to know what class (via the targetEntity command) and what variable (inversedBy=) on that class to 'work backwards' so to speak and get the category information from. An easy way to remember this, is the class that would have the foreignkey_id is the one that needs to have inversedBy.
Where as with category, and its $tasks property (which is not on the table remember, just only part of the class for optimization purposes) is MappedBy 'tasks', this creates the relationship officially between the two entities so that doctrine can now safely use JOIN SQL statements instead of two separate SELECT statements. Without mappedBy, the doctrine engine would not know from the JOIN statement it will create what variable in the class 'Task' to put the category information.
Hope this explains it a bit better.
In bidirectional relationship has both an owning side and an inverse side
mappedBy : put into The inverse side of a bidirectional relationship To refer to the field in the owning side of entity
inversedBy : put into The owning side of a bidirectional relationship To refer to the field on the inverse side of entity
AND
mappedBy attribute used with the OneToOne, OneToMany, or ManyToMany mapping declaration.
inversedBy attribute used with the OneToOne, ManyToOne, or ManyToMany mapping declaration.
Notice :
The owning side of a bidirectional relationship the side that contains the foreign key.
there two reference about inversedBy and mappedBy into Doctrine Documentation :
First Link,Second Link
5.9.1. Owning and Inverse Side
For Many-To-Many associations you can chose 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 create Article 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:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html

Categories