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();
Related
After a lot of searching for indirect associations, I only came up with this question. What I'd like to know is the exact opposite of that question, so I'm building up on it. Given the same entities:
class Continent {
/**
* #ORM\Id
* #ORM\Column(type="integer", name="id")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Country", mappedBy="continent")
*/
private $countries;
}
class Country {
/**
* #ORM\Id
* #ORM\Column(type="integer", name="id")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Continent", inversedBy="countries")
* #ORM\JoinColumn(name="continent_id", referencedColumnName="id")
*/
private $continent;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\City", mappedBy="country")
*/
private $cities;
}
class City {
/**
* #ORM\Id
* #ORM\Column(type="integer", name="id")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Country", inversedBy="cities")
* #ORM\JoinColumn(name="country_id", referencedColumnName="id")
*/
private $country;
}
Is there any way to have a collection of all cities in a continent, without having to build a custom query every time, with a field in the same fashion as the countries collection, possibly via annotations or maybe dynamic mapping?
The only way I could think of is by overriding the find... methods in the Repository - which I'm not sure how exactly I could achieve adding the field - and then either looping over the countries and adding their cities to a new collection, or using a custom query entirely.
First a comment about your mapping. Your mapping is wrong here:
inversedBy="countries" not inversedBy="country" and probably you want a column named continent_id instead of continentt_id
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Continent", inversedBy="countries")
* #ORM\JoinColumn(name="continent_id", referencedColumnName="id")
*/
private $continent;
And here it should be inversedBy="cities" not inversedBy="city":
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Country", inversedBy="cities")
* #ORM\JoinColumn(name="country_id", referencedColumnName="id")
*/
private $country;
Consider using validation for your entity model, the validation tool that ships with doctrine ORM will help you to get these things correct.
You can get such a result set by inner joining the countries with a certain continent.
Inside your CityRepository:
use Doctrine\ORM\Query\Expr\Join;
//...
public function findCitiesByContinent($params){
$queryBuilder = $this->createQueryBuilder('c')
->innerJoin('c.country', 'cc', Join::WITH, 'cc.continent = :continent')
->setParameter('continent', $params['continent']);
$query = $queryBuilder->getQuery();
return $query->getResult();
}
You can reuse the query and just pass a different value for continent to your $params array each time you want the cities for a different continent.
This code is not tested, but I think it should work. Just leave a comment if you run into issues while testing this.
You can also add methods like #Edwin suggested, but you have to realize that it will eagerly load all entities. This is killing for performance if you have a huge amount of rows in your tables (a lot of cities and countries). That is why a query in your repository would be better.
Alternatively you can do this inside your entities by using criteria and collection filtering. You can read on how to do this here in the documentation chapter 8.8. Filtering Collections
The advantage of using a filter is also well explained in the documentation:
If the collection has not been loaded from the database yet, the filtering API can work on the SQL level to make optimized access to large collections.
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.
At the beginning sorry for my poor English, I hope you understand me. I'm writing a simple portal in Symfony2 and came to the point where it needs to make relationships between tables with MySQL, all the ways of the internet browsed, tested and nothing came of it. The tables below.
http://i.stack.imgur.com/vR77x.png
http://i.stack.imgur.com/GDXDw.png
Now yes, by getting the user from the database, I would like to once stretched to the profession (vocation), but together with its name, is even an option?
If I understand correctly you want to create a OneToOne relationship between your Entities?!
On the Player entity:
/**
* Player
*
* #ORM\Table(name="players")
* #ORM\Entity()
*/
class Player
{
/**
* #ORM\OneToOne(targetEntity="Vocation", inversedBy="player")
* #ORM\JoinColumn(name="vocation", referencedColumnName="id")
*/
private $vocation;
...
}
And at the Vocation one
/**
* Vocation
*
* #ORM\Table(name="vocations")
* #ORM\Entity()
*/
class Vocation
{
/**
* #ORM\OneToOne(targetEntity="Player", mappedBy="vocation")
*/
private $player;
/**
* #var string
*/
private $vocationName;
/**
* #var integer
*/
private $id;
...
}
Something like this?
Also (from looking at your tables) maybe you possibly want a ManyToOne relationship instead of a OneToOne?
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).