How to have foreign keys each other on doctorine2 - symfony

I have two tables named 'Lesson' and 'MutorSche'
MutorSche has columns 'lessonBooked'
Lesson has columns 'booked'
I want to have foreign key by each other.
But I am not sure the meaning of inversedBy and mappedBy.
These are my cords.
Is there anything wrong??
please help me .thanks..
/**
*
* #ORM\OneToMany(targetEntity="Acme\UserBundle\Entity\Lesson", inversedBy="booked*removethis : name of the variable in Lesson.php*")
* #ORM\JoinColumn(name="lessonBooked", referencedColumnName="id")
*/
private $lessonBooked = null;
/**
*
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\MutorSche", mappedBy="lessonBooked*removethis : name of the variable in MutorSche.php*")
*/
private $booked;

foreign Keys are added on the owning side ( from doctrine's pov - not always what you consider being the owning side ) aka the side using inversedBy.
Which foreign key ( i.e. name="id ) plus the column name ( i.e. referencedColumnName="user_id" ) to add can be configured using the #JoinColumn annotation.
#ManyToOne is always the owning side of the relation.
Logically you can't add all foreign keys to one database entry in a column. those have to be stored on every single one of the "many" related entries.
Doctrine does not add foreign keys on both sides.

Related

doctrine nested entities cascade persist : how to reuse existing entities

If entity A contains multiple entity B and has cascade:persist, how to reuse existing entities B when persisting ?
B entity has one primary key, an integer, and the id of the A parent. The only data it contains is the primary key.
Example:
A has 2 B entities, identified by their id, 14 and 23.
A.Bs = [{id=14, AId=A.id}, {id=23, AId=A.Id}]
Now if I modify this managed entity, to add a B entity to A, with id = 56.
A.Bs = [{id=14, AId=A.id}, {id=23, AId=A.Id}, {id=56}]
Relationships
Entity A
/**
* #var B[]|ArrayCollection
*
* #ORM\OneToMany(targetEntity="B", mappedBy="A", cascade={"persist", "remove"}, orphanRemoval=true)
* #Assert\Valid
*/
private $Bs;
Entity B
/**
* #var A
*
* #ORM\ManyToOne(targetEntity="A", inversedBy="Bs")
* #ORM\JoinColumn(name="A_id", referencedColumnName="A_id")
* #Assert\NotNull()
*/
private $A;
If I try to persist I get Integrity constraint violation, because Doctrine tries to persist the existing entities, that have id 14 and 23.
I understand this is expected behaviour, but how can I make it persist new entities, and reuse existing ones ?
More details:
If I get an existing entity A with $em->find($id) and directly use persist and flush, I will get UniqueConstraintException because it tries to persist the already persisted B entities.
Example code:
/** #var A $existingEntityA */
$existingEntityA = $this->getEntity($id);
$this->serializerFactory->getComplexEntityDeserializer()->deserialize(json_encode($editedEntityADataJson), A::class, 'json', ['object_to_populate' => $existingEntityA]);
$this->entityValidator->validateEntity($existingEntityA);
$this->_em->flush();
Example error : Integrity constraint violation: 1062 Duplicate entry '777111' for key 'PRIMARY'
If I understand your example properly - you're doing something like this:
$b = new B();
$b->setId(56);
$a->getB()->add($b);
and you having a row with primary key 56 into database table that is represented by B?
If my assumption is correct - it is wrong way to go. Reason is that Doctrine internally stores so called "identity map" that keeps track of all entities that either being fetched from database or persisted by calling EntityManager::persist(). Every entity that is scheduled for commit but not available into identity map is considered as "new" and scheduled for insertion. If row with same primary key is already available in database - you're receiving UniqueConstraintException.
Doctrine doesn't handle a case "let me look if there is an entity with such primary key in database" by itself because it will hurt performance significantly and is not needed in most cases. Each such test will result into database query, imagine if you will have thousands of such entities. Since Doctrine doesn't know business logic of your application - it will spend even more resources with attempts to guess optimal strategy so this is intentionally left out of scope.
Correct way for you would be to get your entity by itself before adding to collection:
$newB = $em->find(B::class, 56);
if ($newB) {
$a->getB()->add($newB);
}
In this case new entity will internally have "managed" status and will be correctly handled by Doctrine at a time of commit.

Symfony 4 how to delete entity from OneToMany relationship

I'm having a bit of a problem deleting an entity assigned to another with a OneToMany relationship.
I have an entity called Business and it has a property "units" which is a collection of Unit entities on a OneToMany relationship (business can have many units).
When i try to delete a single unit from the database i get a violation of the foreign keys, it won't let me remove the unit from the business entity.
Here is a condensed version of both entities:
BUSINESS
/**
* #ORM\Entity(repositoryClass="App\Repository\BusinessRepository")
*/
class Business
{
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="App\Entity\Unit", mappedBy="business")
*/
private $units;
}
UNIT
/**
* #ORM\Entity(repositoryClass="App\Repository\UnitRepository")
*/
class Unit
{
/**
* #var Business
* #ORM\ManyToOne(targetEntity="App\Entity\Business", inversedBy="units")
* #ORM\JoinColumn(name="business_id", referencedColumnName="id")
*/
private $business;
}
So in the UnitRepository i have a delete method:
/**
* #param Unit $unit
*/
public function delete(Unit $unit){
$this->em->remove($unit);
$this->em->flush();
}
And i get this error:
An exception occurred while executing 'DELETE FROM unit WHERE id = ?' with params [1]:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`businessdirectory`.`unit_day`, CONSTRAINT `FK_F03D80CEF8BD700D` FOREIGN KEY (`unit_id`) REFERENCES `unit` (`id`))
I don't know if i have set up the relationship incorrectly or not here, but i should be able to delete a single unit from a business, and i should be able to delete the entire business with it's units.
See if a Unit entity is the owning side of another relationship. At that point you would need to delete all the entities that depend on Unit first. You can freely delete the owned side of a One-To-Many relationship but you would need to clear all owned elements before deleting the owning side.

Definitions of one-to-many, many-to-many etc

I'm building a database application using Doctrine2. I'm getting somewhat confused by the foreign key mappings. I'm wondering, have I got these examples correct:
One-To-One: An X has exactly one Y.
One-To-Many: An X can have multiple Ys.
Many-To-One: Multiple Xs can have the same Y.
Many-To-Many: Multiple Xs can have multiple Ys.
This is the specific situation that got me confused:
A User has exactly one HomeTown. Many users can belong to the same home town, so the link for the User is:
/**
* #ORM\ManyToOne(targetEntity="HomeTown", inversedBy="localUsers")
*/
$homeTown;
And, the corresponding HomeTown link is:
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="homeTown")
*/
$localUsers;
OR is it:
/**
* #ORM\OneToMany(targetEntity="User", mappedBy="homeTown")
*/
$localUsers;
Some clarification would be much appreciated!
I've been looking at http://doctrine-orm.readthedocs.org/en/latest/reference/association-mapping.html
When you have OneToMany association, the inverted has to be ManyToOne. Saying that, your second option is correct.
TIP: Using Doctrine CLI command orm:validate-schema might also help to identify this issue.
The full path in Symfony app: php app/console doctrine:schema:validate
If you want one city to have many users the mapping should be as it follows
Entity City
/**
* #ORM\OneToMany(targetEntity="User", mappedBy="homeTown")
*/
private $users;
...
public function __construct()
{
$this->users = new ArrayCollection();
}
...
Entity User
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="users")
* #ORm\JoinColumn(name="home_town", referencedColumnName="id")
*/
private $homeTown;
This mapping shows us that the City which is owning side has On-To-Many relation with User(target Entity). Respectively the User which is inversed side has to be annotated with ManyToOne relation because many users have same city. Of course, here the target entity should be City. Its important to specify which column is pointing the foreignkey with the referencedColumnName attribute in JoinColumn annotation. It shows what column of other table points this key. In this example in table User there is column named "home_town" which is a foreign key pointing to column id of table City
In ManyToOne relation you shod use JoinColumn annotation
This mapping is also Bidirectional.
You can make id Unidirectional as in the User Entity do not use "inversedBy=" attribute and remove OneToMany annotation with $user property from the City entity. It is something like when you have to know the city of a particular user, but you do not need to know all users for a specific city

Symfony2 Many-to-Many relationship sharing one JoinTable

I want to make a Many-to-Many relationship which shares the same join table. I tried the following:
<?php
/** #Entity **/
class User
{
// ...
/**
* #ManyToMany(targetEntity="Group", inversedBy="users")
* #JoinTable(name="users_groups")
**/
private $groups;
// ...
}
/** #Entity **/
class Group
{
// ...
/**
* #ManyToMany(targetEntity="User", mappedBy="groups")
* #JoinTable(name="users_groups")
**/
private $users;
// ...
}
This, however, returns the following error when I try to update the tables:
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'postgres.user_groups' already exists.
How do I create a many-to-many relationship that shares the same table 'user_groups'?
Note: I understand that I can remove the #JoinTable(name="users_groups") but when I do this I no longer have a Many-to-Many relationship with two owning sides. Instead only one side (owning side) knows about the join table.
Remove #JoinTable(name="users_groups") annotation from your inverse side entity that is Group, Once owning side entity has mapping information then there is no need to define again in inverse side entity, some of the key point related to your question
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.
You can pick the owning side of a many-to-many association yourself
Reference Bidirectional Associations
class Group
{
/**
* #ManyToMany(targetEntity="User", mappedBy="groups")
**/
private $users;
}
See Many-To-Many, Bidirectional
example from documentation

Many-to-one custom fields - unique

I'm using a foreign table to join 2 entities, exactly this way :
http://www.prowebdev.us/2012/07/symfnoy2-many-to-many-relation-with.html
I'd like to understand why do we need an ID as primary key in the foreign table ?
I'd rather take the couple of foreign keys as the primary keys, this way I make sure that there is no double entries for the same relation. no ?
I think Doctrine just wants every table to have and Id field, you can however force Doctrine the check if the combination of foreign keys is unique:
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
...
/*
* #UniqueEntity({"foreign_key1", "foreign_key2"})
* ...
*/
class JoinTable
{
...
}

Resources