I am trying to create a many-to-many foreign key table relation targeting the same entity.
I have successfully created relations with other entities but I'm having trouble when targeting the same entity.
I read a few stack overflow questions and answers and saw what I was doing was possible..
I based my class of this example - the table unit_relations is empty when i add a parent, but works when I add a child to a unit. I'm assuming because the field has the inversedBy annotation.
What do I need to add to allow bi-drectional updating/inserting?
I tried swapping the joinColums like this answer - nothing...
--
/**
* Unit
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Acme\DemoBundle\Entity\UnitRepository")
*/
class Unit
{
....
/**
* #ORM\ManyToMany(targetEntity="Unit", inversedBy="parents")
* #ORM\JoinTable(name="unit_relations",
* joinColumns={#ORM\JoinColumn(name="unit_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="child_unit_id", referencedColumnName="id")}
* )
*/
private $children;
/**
* #ORM\ManyToMany(targetEntity="Unit", mappedBy="children")
*/
private $parents;
}
is adding inversedBy instead of having mappedBy, with the join columns also swapped, the proper way to do it?
Can someone explain this?
/**
* #ORM\ManyToMany(targetEntity="Unit", inversedBy="parents")
* #ORM\JoinTable(name="unit_relations",
* joinColumns={#ORM\JoinColumn(name="unit_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="child_unit_id", referencedColumnName="id")}
* )
*/
private $children;
/**
* #ORM\ManyToMany(targetEntity="Unit", inversedBy="children")
* #ORM\JoinTable(name="unit_relations",
* joinColumns={#ORM\JoinColumn(name="child_unit_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="unit_id", referencedColumnName="id")}
* )
*/
private $parents;
EDIT
as requested here's the code showing how I create the relation. I just realized it may because the entity doesn't have an id yet since I'm adding the relation on its creation...
$unit = new \Acme\DemoBundle\Entity\Unit();
/* #var $parentUnit \Acme\DemoBundle\Entity\Unit */
$parentUnit = $this->getDoctrine()->getRepository('AcmeDemoBundle:Unit')->findById($request->get('unitId'));
$unit->addParent($parentUnit);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($unit);
$entityManager->flush();
It doesn't matter, that there is no ID, when you add a parent relations.
I cant in detail explain why this happens, but i think the main problem is, that in Many-To-Many Self-Referencing the attribute with JoinTable annotation is potentially the Master-Entity. You can say, that it "holds" all other relations to this Entity.
You can recieve the bi-directional updating/inserting while changing the function $unit->addParent($parent). Change it as follows:
public function addParent($parent)
{
$this->parents[] = $parent;
$parent->addChild($this); // Add the relation in the proper way
}
That should work fine!
Regards!
Related
I'm working on a Symfony project (V3.4) and I'm using an existing DB (can't change it).
To interact with it I use doctrine annotations and the work is well done!
I manage to submit requests using JoinTable and JoinColumns but there is one last thing I don't know how to deal with ...
I have the following tables :
tables
I have the id from table A and I'm trying to get the libelle from the E table.
Is there a way to do it using annotations? For now I've already done it between 3 tables but I don't know how to do it for more :
#ORM\ManyToMany(targetEntity="")
* #ORM\JoinTable(name="",joinColumns={#ORM\JoinColumn(
name="",referencedColumnName="")}, inverseJoinColumns={#ORM\JoinColumn(
name="",referencedColumnName="", unique=true)})
If it's not possible I'm open to suggestions.
Thanks!
By looking at your need and your schema, i can tell you that you don't need many to many relationship. You should bidirectional relation across the entities(tables).
I don't know which kind of relation you have right now, but assuming one-to-one, you can setup relations following way:
EntityA
/**
* #OneToOne(targetEntity="EntityB", mappedBy="entityA")
*/
private $entityB;
EntityB
/**
* #OneToOne(targetEntity="EntityA", inversedBy="entityB")
* #JoinColumn(name="entityA_id", referencedColumnName="id")
*/
private $entityA;
/**
* #OneToOne(targetEntity="EntityC", mappedBy="entityB")
*/
private $entityC;
EntityC
/**
* #OneToOne(targetEntity="EntityB", inversedBy="entityC")
* #JoinColumn(name="entityB_id", referencedColumnName="id")
*/
private $entityB;
/**
* #OneToOne(targetEntity="EntityD", mappedBy="entityC")
*/
private $entityD;
EntityD
/**
* #OneToOne(targetEntity="EntityC", inversedBy="entityD")
* #JoinColumn(name="entityC_id", referencedColumnName="id")
*/
private $entityC;
/**
* #OneToOne(targetEntity="EntityE", mappedBy="entityE")
*/
private $entityE;
EntityE
/**
* #OneToOne(targetEntity="EntityD", inversedBy="entityE")
* #JoinColumn(name="entityD_id", referencedColumnName="id")
*/
private $entityD;
I didn't have time to test it. But it should be straight forward.
You can get libelle from E table following way:
$entityA = $entityRepositoryForA->find(a_id_here);
$entityA->getEntityB()->getEntityC()->getEntityD()->getEntityE->getLibelle();
I currently have to Entities in my application:
Page
Block
A Page can have many Blocks, which are shared across many Pages, so it is quite obvious that the relation is a ManyToMany. However, I need to be able to add the same Block twice (or more) to the same Page. Doctrine creates the "page_block" join table automatically, but with page_id and block_id both as Primary Keys, therefore adding a duplicate throws an error.
Is it possible, without adding an additional Entity, to tell doctrine to allow duplicates on the Page--Block relation ?
Well, I'm not sure about that behavior in doctrine, but if that is the case, then you can do something that I almost always do. Represent the ManyToMany relation as two OneToMany-ManyToOne. You must create your own PageBlock entity and configure it's foreign keys.
class Page{
/**
* #var array
*
* #ORM\OneToMany(targetEntity="PageBlock", mappedBy="page", cascade={"all"})
*/
private $pageBlocks;
}
class Block{
/**
* #var array
*
* #ORM\OneToMany(targetEntity="PageBlock", mappedBy="block", cascade={"all"})
*/
private $pageBlocks;
}
class PageBlock{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \stdClass
*
* #ORM\ManyToOne(targetEntity="Page", inversedBy="pageBlocks")
* #ORM\JoinColumn(name="id_page", referencedColumnName="id")
*/
private $page;
/**
* #var \stdClass
*
* #ORM\ManyToOne(targetEntity="Block", inversedBy="pageBlocks")
* #ORM\JoinColumn(name="id_block", referencedColumnName="id")
*/
private $block;
}
As you can see the primary key remains as ID, so problem resolved. I say almost always do because this is how I do it if I need an extra attribute in the relation(almost always it happens). I suspect that could be a way of do it with the ManyToMany annotation, but there is no difference with this approach.
Hope this help you.
I have two ManyToMany relations in one entity.
First:
/**
* #ORM\ManyToMany(targetEntity="RFQItem")
* #ORM\JoinTable(name="rfqitem_to_rfq",
* joinColumns={#ORM\JoinColumn(name="rfqitem", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="id", referencedColumnName="id")}
* )
*/
protected $rfqitems;
Second:
/**
* #ORM\ManyToMany(targetEntity="RFQItem", cascade={"persist"})
* #ORM\JoinTable(name="new_rfqitem_to_rfq",
* joinColumns={#ORM\JoinColumn(name="rfqitem", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="id", referencedColumnName="id")}
* )
*/
protected $rfq_item;
Problem is that I can't generate my entities and update schema, because generating entities gives me dublicated methods:
/**
* Add rfq_item
*
* #param \RFQ\IronilBundle\Entity\RFQItem $rfqItem
* #return RFQ
*/
public function addRfqItem(\RFQ\IronilBundle\Entity\RFQItem $rfqItem)
{
$this->rfq_item[] = $rfqItem;
return $this;
}
which throws an error:
Fatal error: Cannot redeclare RFQ\IronilBundle\Entity\RFQ::addRfqitem()
I need these two relations in one entity, because one is for adding previously made RFQitems, but second is to create new RFQitems and this all is in one form.
What I can do resolve this?
Doctrine generate methods with names based on your fields. Your fields are very similar: Doctrine will omit ending "s" and underscores, convert strings to lowercase. Your fields will look like $rfqitem and $rfqitem. As you can see Doctrine will see your fields with two absolutely equal names. You need to rename one of your fields $rfqitems, $rfq_item.
Try something more semantic like: $oldRFQItems and $newRFQItems.
OK, if anyone can help me with this that would be great, because it appears to be intractable.
I have 2 entities set up in a new zf-boilerplate project as below. I am trying to follow the tutorial on Zendcasts.com - One-to-Many with Doctrine 2, but can't get doctrine to recognise the associations I have mapped. If I run orm:schema-tool:create --dump-sql, it dumps the generated Sql, but NOT the ALTER TABLE statements at the end which should would create the Foreign Key Mapping, I can't get that to work properly.
I've tried everything I can think of, the JOIN statement I need to run obviously doesn't work either, but I figure if I can just get Doctrine to recognise the ALTER statement I can carry it from there.
Any ideas would be great, let me know if you need more info. I thought at first maybe the .ini file might be set up wrong, but I think this is more something to do with the relationship annotation?
Library/Photo/Entity/Gallery.php
<?php
namespace Photo\Entity;
/**
* #Entity(repositoryClass="Photo\Entity\Repository\MyGallery")
* #Table(name="gallery")
*/
class Gallery {
/**
* #Id #GeneratedValue
* #Column(type="smallint",nullable=false)
* #var integer
* #OneToMany(targetEntity="Photo", mappedBy="galleryID")
*/
protected $id;
/**
* #Column(type="string", length=200)
* #var string
*/
protected $gallery;
Library/Photo/Entity/Photo.php
<?php
namespace Photo\Entity;
/**
* #Entity(repositoryClass="Photo\Entity\Repository\MyPhoto")
* #Table(name="photo")
*/
class Photo {
/**
* #Id #GeneratedValue
* #Column(type="smallint",nullable=false)
* #var integer
*/
protected $id;
/**
* #Column(type="smallint",nullable=false)
* #var integer
* #ManyToOne(targetEntity="Gallery")
* #JoinColumns({
* #JoinColumn(name="gallery_id", referencedColumnName="id")
* })
*/
protected $galleryID;
Hmm... I see.. Check you column names, gallery_id vs galleryID looks suspicious.
If it is gallery_id, then you have to change the $galleryID annotation to #Column(type="smallint", nullable=false, name="gallery_id")
Generally, everywhere in the object model you should use the object field names, for example mappedBy="galleryID", but the column itself should be mapped with the appropriate DB name, like I mentioned #Column(name="gallery_id"), or for example #JoinColumns({#JoinColumn(name="gallery_id" referencedColumnName="id")})
I'm currently trying to map music related data using Doctrine2's POPO Annotations.
I haven't had problems mapping any other many-to-many relations, but one specific relation is giving me trouble. It does not throw an error, but the mapping does not get inserted into the mapping table (artist_album)
Artist:
<?php
/**
* #orm:Entity
* #orm:Table(name="artist")
*/
class Artist
{
...
/**
* #orm:ManyToMany(targetEntity="Company\MusicBundle\Entity\Album", inversedBy="artists", cascade={"persist"})
* #orm:JoinTable(name="artist_album",
* joinColumns={#orm:JoinColumn(name="artist_id", referencedColumnName="id")},
* inverseJoinColumns={#orm:JoinColumn(name="album_id", referencedColumnName="id")})
*
* #var ArrayCollection
*/
private $albums;
...
}
Album
....
/**
* #orm:ManyToMany(targetEntity="Company\MusicBundle\Entity\Artist", mappedBy="albums", cascade={"persist"})
*
* #var ArrayCollection
*/
private $artists;
...
}
I'm sure it's just something in I've done wrong in the mapping, but I just can't put my proverbial finger on it.
My problem was that I was setting the artist on the inverse side of the relationship. It appears you must set the relationship on the owning side (in this case, Artist).