I have a manytomany relation between two entities (product and quote) so that one or more products could be in one or more quotes. For example:
assuming that a customer chooses two products from a specific enterprise and he would like to receive a quote that summarize all the chosen products. Then he decides to choose another product of another enterprise to get at the end another quote. So here we have two quotes of different enterprises and of same user that each quote has its own products.
you will say that it is a onetomany relation between the quote and the product because as I mentioned above that one quote can have many products so in the database you will have a quote_id column in the product table.
However, if another customer chooses the same products, a new quote will be created but when the query will insert these products in the quote by filling the quote_id column of the product table, it finds out that these articles are already have a quote_id of another quote.
That's why it is a manytomany relation so that many articles could be in many quotes.
This part is realized and I can match many products to many quotes without any problems.
Here is the entity of quote:
/**
* #ORM\ManyToMany(targetEntity="ArticleBundle\Entity\Article", mappedBy="quotes")
*/
private $articles;
public function __construct() {
$this->articles = new \Doctrine\Common\Collections\ArrayCollection();
$this->createdAt = new \DateTime();
}
/**
* Set articles
*
* #param \ArticleBundle\Entity\Article $articles
* #return Quote
*/
public function setArticles(\ArticleBundle\Entity\Article $articles = null) {
$this->articles = $articles;
return $this;
}
/**
* Get articles
*
* #return \ArticleBundle\Entity\Article
*/
public function getArticles() {
return $this->articles;
}
/**
* Add articles
*
* #param \QuoteBundle\Entity\Quote$articles
* #return Devis
*/
public function addArticle(\ArticleBundle\Entity\Article $article) {
$article->addQuote($this); // synchronously updating inverse side
$this->articles[] = $article;
return $this;
}
/**
* Remove articles
*
* #param \QuoteBundle\Entity\Quote $articles
*/
public function removeArticle(\ArticleBundle\Entity\Article $article) {
$this->articles->removeElement($article);
}
}
the entity article:
/**
* #ORM\ManyToMany(targetEntity="QuoteBundle\Entity\Quote", inversedBy="articles")
* #ORM\JoinTable(name="quotes_articles")
*/
private $quotes;
/**
* Add devises
*
* #param \DevisBundle\Entity\Quote $quote
* #return Article
*/
public function addQuote(\QuoteBundle\Entity\Quote $quote) {
$this->quotes[] = $quote;
return $this;
}
/**
* Remove quotes
*
* #param \QuoteBundle\Entity\Quote $quote
*/
public function removeQuote(\QuoteBundle\Entity\Quote $quote) {
$this->quotes->removeElement($quote);
}
/**
* Get quotes
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getQuotes() {
return $this->quotes;
}
}
The part that I find it difficult to reach is that when a customer chooses only one product , I would like to let him specify the quantity of the chosen product.
so when I updated the doctrine schema I got these tables :
quote table
product table
quotes_products table
can anyone tell me in which table I must add the quantity column and how can I write the annotation to let it added automatically in the database . I think it will be added to the quotes_products table.
Thank you for helping me.
when adding a quantity column to the join table, you are making of your relationship a non pure manytomany. So, there will be no problem in separating the manyToMany relationships to two oneToMany relationships, and create an Entity Class for the join with additional field quantity.
The idea in pseudo code:
Class Quote
{
private $products;
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="quote")
*/
}
Product:
Class Product
{
private $quotes;
/**
* #ORM\OneToMany(targetEntity="Quote", mappedBy="product")
*/
}
ProdcutQuote:
Class ProductQuote
{
/**
* #ORM\ManyToOne(targetEntity="Quote")
* #ORM\JoinColumn(name="quote_id", referencedColumnName="id")
*/
private $quote;
/**
* #ORM\ManyToOne(targetEntity="Product")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
private $quantity;
}
Related
I want to be able to select a school (that has its own entity) while creating a mission (also has its entity)
Since a school can have several missions, and you can select several schools at the mission's creation, I used a ManyToMany.
The problem is that after creating this "ManyToMany", generating the entities and updating my schema, Symfony created a table, but left it totally empty, without the two columns that I asked for. I'm not really used to Symfony nor to the ManyToMany system, so I might have done some mistake without noticing it, still I find this weird.
Here's the interesting part of my ecole (school) entity:
class Ecole{
// ...
/**
* #ORM\ManyToMany(targetEntity="MissionBundle\Entity\Mission", mappedBy="ecolesDispo")
*/
protected $missionsDispos;
// ...
/**
* Add missionsDispo
*
* #param \MissionBundle\Entity\Mission $missionsDispo
*
* #return Ecole
*/
public function addMissionsDispo(\MissionBundle\Entity\Mission $missionsDispo)
{
$this->missionsDispos[] = $missionsDispo;
return $this;
}
/**
* Remove missionsDispo
*
* #param \MissionBundle\Entity\Mission $missionsDispo
*/
public function removeMissionsDispo(\MissionBundle\Entity\Mission $missionsDispo)
{
$this->missionsDispos->removeElement($missionsDispo);
}
/**
* Get missionsDispos
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getMissionsDispos()
{
return $this->missionsDispos;
}
And here is the interesting part of my mission entity:
/**
* #ORM\ManyToMany(targetEntity="EcoleBundle\Entity\Ecole", inversedBy="missionsDispo")
* #ORM\JoinTable(name="Mission2Ecole",
* joinColumns={#ORM\JoinColumn(name="em_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="me_id", referencedColumnName="id")}
* )
*/
protected $ecolesDispo;
// ...
/**
* Constructor
*/
public function __construct()
{
$this->ecolesDispo = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add ecolesDispo
*
* #param \EcoleBundle\Entity\Ecole $ecolesDispo
*
* #return Mission
*/
public function addEcolesDispo(\EcoleBundle\Entity\Ecole $ecolesDispo)
{
$this->ecolesDispo[] = $ecolesDispo;
return $this;
}
/**
* Remove ecolesDispo
*
* #param \EcoleBundle\Entity\Ecole $ecolesDispo
*/
public function removeEcolesDispo(\EcoleBundle\Entity\Ecole $ecolesDispo)
{
$this->ecolesDispo->removeElement($ecolesDispo);
}
/**
* Get ecolesDispo
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEcolesDispo()
{
return $this->ecolesDispo;
}
After all this was created, I was supposed to get a multi selector with the list of all the schools saved in the database (I already added it to the missionType file), but I get absolutely nothing.
I don't really know if I inverted the annotations, or if the "joinTable" part is correct, but I'm completely lost here.
Does anyone have an idea?
Thank you in advance
Just wrong typo "s"? inversedBy="missionsDispo" >>> inversedBy="missionsDispos"
PS. Official doc here
http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/association-mapping.html#many-to-many-bidirectional
I have this two tables (see pics below) mapped as follow:
class Brand
{
...
/**
* #var Company
*
* #ORM\ManyToOne(targetEntity="Company")
* #ORM\JoinColumn(name="companies_id", referencedColumnName="id")
*/
protected $company;
}
class Company
{
...
}
I need to add support for add a new Brand from Company but I have not idea in how to achieve this. This are handled through SonataAdminBundle but I think I need to add something else to entities in order to create brands from company but I am not sure what this would be, can I get some help? I am stucked
1st attempt
After get an answer this is how I modify Company entity:
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
class Company
{
...
/**
* #var Brand
* #ORM\OneToMany(targetEntity="Brand", mappedBy="company", cascade={"persist"})
**/
protected $brands;
public function __construct()
{
$this->brands = new ArrayCollection();
}
...
public function getBrands()
{
return $this->brands;
}
/**
* Add brands
*
* #param Brand $brand
* #return Brands
*/
public function addBrand( Brand $brand)
{
$this->brands[] = $brand;
return $this;
}
/**
* Remove brands
*
* #param Brand $brand
*/
public function removeBrand( Brand $brand)
{
$this->brands->removeElement($brand);
}
}
But I am getting this error:
No entity manager defined for class
Doctrine\Common\Collections\ArrayCollection
Why is that?
You could try setting up your entities like this:
class Brand
{
/**
* #var Company
*
* #ORM\ManyToOne(targetEntity="Company", inversedBy="brands")
* #ORM\JoinColumn(name="companies_id", referencedColumnName="id")
*/
protected $company;
}
class Company
{
/**
* #var ArrayCollection
*
* #OneToMany(targetEntity="Brand", mappedBy="company", cascade={"persist"})
**/
protected $brands;
}
What we're defining here is that new Brands can be created from the Company entity with cascade={"persist"}.
It's recommended you implement addBrand and removeBrand in Company for direct interaction with the ArrayCollection.
A simple example of the final functionality:
$company = $service->getCompany(1); // our company entity
$brand = new Brand();
$brand->set...
...
$company->addBrand($brand);
$entityManager->persist($company);
EDIT
This is just an example, you may choose not to add with keys or even implement a remove function, but this is a starting point:
public function addBrand(Brand $brand)
{
// key needs to be something that can uniquely identify the brand
// e.g. name
$this->getBrands()->set(*key*, $brand);
return $this;
}
public function removeBrand($key)
{
$this->getBrands()->remove($key);
return $this;
}
I've a ManyToMany relationship between Pais and FabricanteDistribuidor tables defined as follow:
Pais.php
class Pais
{
// column definitions
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\FabricanteDistribuidor", inversedBy="paises", cascade={"persist"})
* #ORM\JoinTable(name="negocio.fabricante_distribuidor_pais", schema="negocio",
* joinColumns={#ORM\JoinColumn(name="fabricante_distribuidor_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="pais_id", referencedColumnName="id")}
* )
*/
protected $fabricanteDistribuidor;
/**
* Add fabricanteDistribuidor
*
* #param AppBundle\Entity\FabricanteDistribuidor $fabricanteDistribuidor
*/
public function addfabricanteDistribuidor(\AppBundle\Entity\FabricanteDistribuidor $fabricanteDistribuidor)
{
$this->fabricanteDistribuidor[] = $fabricanteDistribuidor;
}
/**
* Get fabricanteDistribuidor
*
* #return Doctrine\Common\Collections\Collection
*/
public function getfabricanteDistribuidor()
{
return $this->fabricanteDistribuidor;
}
}
FabricanteDistribuidor.php
class FabricanteDistribuidor
{
// column definitions
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Pais", mappedBy="fabricanteDistribuidor", cascade={"persist"})
*/
protected $paises;
public function __construct()
{
$this->paises = new ArrayCollection();
}
/**
* Set paises
*
* #param AppBundle\Entity\Pais $pais
* #return FabricanteDistribuidor
*/
public function addPaises(\AppBundle\Entity\Pais $pais)
{
$this->paises[] = $pais;
return $this;
}
/**
* Get paises
*
* #return Doctrine\Common\Collections\Collection
*/
public function getPaises()
{
return $this->paises;
}
}
That will generate a table fabricante_distribuidor_pais on the schema negocio with fabricante_distribuidor_id and pais_id FK pointing to the PK on the related tables, that's fine.
Regarding this scenario:
1- It's possible to define fabricante_distribuidor_id and pais_id as PK on the fabricante_distribuidor_pais table? I mean adding some extra annotation or I need to create a external entity and set them as #ORM\Id on the column definition?
2- Are the addXXX and getXXX methods right in my entities? By right I mean: I should add one or many paises (from Pais entity) to FabricanteDistribuidor easily and I don't care about to the inverse relation meaning I will not add FabricanteDistribuidor from a Pais, are them right or do I need to change something?
1- If one id is a primary key doesn't the relation becomes many to one/ one to many ? Even 1to1 if both are PK
2- If you don't care about the inverse you are going to add getters and setters in only one entity yes. You can still change it to a biredictionnal later with the attribute "mappedBy"
Check if an entity exists :
You can do that in your controller :
for example in Pays
$data = $em->getRepository('AcmeBundle:Pais')->findOneByFabricanteDistribuidor($id);
if($data)
{
// the entity is allready persisted
}
else
{
// no, we can persist the entity
}
I have interface:
interface Product
{
function getAmount();
}
and php doctine entities:
/**
* #ORM\Table(name="orders")
* #ORM\Entity
*/
class Order
{
private $products = array();
public function addProduct(Product $product){
$this->products[] = $product;
}
public function getProducts() {
return $this->products;
}
function getAmount() {
$amount = 0;
foreach ($this->products as $product) {
$amount += $product->getAmount();
}
return $amount;
}
}
/**
* #ORM\Table(name="books")
* #ORM\Entity
*/
class Book implements Product
{
function getAmount() {
return 1;
}
}
/**
* #ORM\Table(name="pens")
* #ORM\Entity
*/
class Pen implements Product
{
function getAmount()
{
return 2;
}
}
Book, Pen - are different entities and table. How to implement relationship Order::products with collection of Books, Pens, etc(for save in database)?
I understand that two solutions to this problem.
The first is when saving(and loading) to the database manually convert this relationship to map(array of entities names and ids).
This decision I do not like.
And the second is to correct architecture. I do not know how. Most likely already have a ready-made solution ... Help please.
i'am not sure you can get it exactly that way.
whether you define a separate Entity Poduct and add a column product_type where you tell whether it book, pen or what ever.
in entity Product that property should be defined as enum and it can be tricky(depends on what do you use besides doctrine)
or you make it for each product type (what can be pretty fast to a nightmare). I'd guess you have ManyToMany Relation. most probably it should look smth. like that.
in Order
/**
* #ManyToMany(targetEntity="Book")
* #JoinTable(name="Order_Book",
* joinColumns={#JoinColumn(name="book_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="order_id", referencedColumnName="id")}
* )
*/
protected $book;
/**
* #ManyToMany(targetEntity="Pen")
* #JoinTable(name="Order_Pen",
* joinColumns={#JoinColumn(name="pen_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="order_id", referencedColumnName="id")}
* )
*/
protected $pen;
and in Book:
/*
* #ManyToMany(targetEntity="Order", mappedBy="book")
*/
protected $order;
with Pen and others the same way.
Take a look at inheritance mapping: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html
I have tow entities, Submission and Submission History. When I am trying to get the SubmissionHistories from a specific submission, it returns only one, even if there are more entries.
Any idea why?
Here are the entities.
//Entity Submission
/**
* #ORM\OneToMany(targetEntity="SubmissionHistory", mappedBy="submission")
*/
protected $histories;
public function __construct()
{
$this->histories = new ArrayCollection();
}
/**
* Get histories
*
* #return Doctrine\Common\Collections\Collection
*/
public function getHistories()
{
return $this->histories;
}
Then,
//Entity SubmissionHistory
/**
* #ORM\ManyToOne(targetEntity="Submission", inversedBy="histories")
* #ORM\JoinColumn(name="hash_key", referencedColumnName="hash_key")
*/
protected $submission;
/**
* Get submission
*
* #return SciForum\Version2Bundle\Entity\Submission
*/
public function getSubmission()
{
return $this->submission;
}
I found the problem:
I setup the #Id to be the hash_key in the SubmissionHistory entity, which is wrong. By doing that, all results have been collapsed as if I grouped them by "Fields.id".
By changing the Id to the id field, things works perfectly fine.