Multiple Composite Primary Keys mapping Doctrine2 - doctrine-orm

I have been searching for the exact mappings for for the DB Schema as shown below
As one can see the product_i18n table has two composite foreign keys (product_id and locale_id).
Now once the product and locale entities are finished, I wanted to insert the data (name and description) into the product_i18n table.
Is there an example which is similar to this type of mapping using Doctrine 2. Or in case if some one can give a brief overview how to approach this type of mapping, then your information is appreciated.
P.S. In case if one requires more information regarding this, then please dont hesitate to ask.

Doctrine 2 supports composite keys natively
/**
* #Entity
*/
class ProductI18N
{
/** #Id #Column(type="string") */
private $product;
/** #Id #Column(type="string") */
private $locale
public function __construct($product, $locale)
{
$this->product= $product;
$this->locale= $locale;
}
Always keep in mind that the composite key MUST be setted before persist the model:
$productI18N = new ProductI18N("Guanabana", "CR");
$em->persist($productI18N );
$em->flush();
For more information about you can see the documentation:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html

If you have a composite key in with field 1 (id autoincrement) and the field 2 (specified) use:
class ProductI18N
{
/** #Id #Column(type="string") #GeneratedValue(strategy="AUTO") */
private $product;
/** #Id #Column(type="string") #GeneratedValue(strategy="NONE") */
private $locale

Related

ManyToOne from multiple entities into one

I have 3 entities:
client
company
contact
client entity can have multiple contact entities, also company can have multiple contact entities.
Contact entity looks like this:
class ContactDetail
{
use Timestampable;
use Bleamable;
CONST TYPE_EMAIL = 'EMAIL';
CONST TYPE_PHONE = 'PHONE';
CONST TYPE_FAX = 'FAX';
CONST TYPE_MOBILE = 'MOBILE';
/**
* #ORM\Id
* #ORM\Column(type="integer", options={"unsigned"=true})
* #ORM\GeneratedValue(strategy="AUTO")
* #Expose
* #JMS\Groups({"ROLE_USER","ROLE_ADMIN"})
*/
private $id;
/**
* #var string
* #Assert\NotNull()
* #ORM\Column(type="string", columnDefinition="ENUM('EMAIL', 'PHONE', 'FAX', 'MOBILE')", nullable=false)
* #Expose
* #JMS\Groups({"ROLE_USER","ROLE_ADMIN"})
*/
private $type;
/**
* #var string
* #ORM\Column(type="string", nullable=false)
* #Assert\NotBlank(message="Please enter proper value")
* #Expose
* #JMS\Groups({"ROLE_USER","ROLE_ADMIN"})
*/
private $value;
(...)
}
The question is:
Should I do it in this way:
client entity:
/**
* #ORM\OneToMany(targetEntity="CoreBundle\Entity\ContactDetail", mappedBy="client")
* #ORM\JoinColumn(name="contactDetail", referencedColumnName="id")
*/
protected $contactDetails;
company entity:
/**
* #ORM\OneToMany(targetEntity="CoreBundle\Entity\ContactDetail", mappedBy="company")
* #ORM\JoinColumn(name="contactDetail", referencedColumnName="id")
*/
protected $contactDetails;
Which will creates two additional columns (company and client) in the contact entity or should I do it in some other way?
Ex.
Create two columns: entityName and entityId and bind it somehow to client and company?
If second is proper way, please tell me how to achieve this.
Consider to solve this using a unidirectional map from Client and Company to Contact. In this case is a little trick because there is no way to create a OneToMany relationship without using mappedBy and map this bidirectional so Contact need to have theses two columns one for client other for company.
Looking after doctrine documentation, the solution may be a little odd here. It's use a ManyToMany relationship unidirectional from Client and Company. And you don't need to have foreign keys in Client nor Company netheir in Contact. But will have to create a new table to intermediate this relationship.
The simple way to go is create a table for each relationship on table "Client_Contact" and other "Company_Contact" each on with to columns, one for client_id/company_id other for contact_id.col

Doctrine 2 self-referenced entity without unnecessary queries

I'm trying that a self-referenced entity stop from querying the database everytime I fetch the children of one object, and get the whole tree in one query.
This is my entity:
/**
* #ORM\Entity(repositoryClass="ExampleRep")
* #ORM\Table(name="example_table")
*/
class Example {
/**
* #ORM\Id
* #ORM\Column(type="integer", nullable=false);
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Example", inversedBy="children")
* #ORM\JoinColumn(name="parent", referencedColumnName="id", onDelete="SET NULL")
*/
private $parent = null;
/**
* #ORM\OneToMany(targetEntity="Example", mappedBy="parent")
*/
private $children;
}
And i'm calling my date using queryBuilder like:
$query = $this->createQueryBuilder('e');
$query->orderBy('e.parent', 'ASC');
$example_data = $query->getQuery()->getResult();
When I cycle my example_data and call getChildren, another query is made, even if that same object, was already called in the query.
I've followed the example here: Doctrine - self-referencing entity - disable fetching of children but when i do it, my getChildren returns nothing.
Is there a way to fetch my data without overloading the database with multiple requests?
If you know the depth of your tree, you can just do a custom dql query and do:
return $this->createQueryBuilder('example')
->addSelect('children')
->leftJoin('example.children', 'children')
->addSelect('subChildren')
->leftJoin('children.children', 'subChildren')
;
Otherwise, and as stated here, you can generate a flat resultset, and then construct the tree from it.
I made this kind of implementation using materialized paths, but nothing forbids you to do it with foreign keys comparison:
https://github.com/KnpLabs/DoctrineBehaviors/blob/master/src/Knp/DoctrineBehaviors/ORM/Tree/Tree.php#L119
https://github.com/KnpLabs/DoctrineBehaviors/blob/master/src/Knp/DoctrineBehaviors/Model/Tree/Node.php#L219

Doctrine2: How to keep custom join table synchronized in a bi-directional Many-to-Many association?

The statement
I'm trying to reproduce the automatic Doctrine mechanism for handling Many-to-Many bidrectional relationships, but introducing a custom join table.
I've already digged into similar questions:
Joining-Table with Metadata Impairs Getters/Setters - Doctrine 2
but it doesn't really help me because it's absolutely unidirectional
doctrine2 many to many self referencing with intermediate details
but this one does not even talk about managing the relations
Doctrine2: Best way to handle many-to-many with extra columns in reference table
is very interesting. However, although the author mentions its bidirectional needs, he doesn't cover the case.
I'm aware that a join table with extra fields is not an association anymore, just a third entity that refers to the two other ones. And from that statement, it's obvious that one cannot expect it to work out-of-the-box as an implicit Many-to-Many association managed by Doctrine.
But i want to have this trio to work as a simple, straight, bidirectional Many-to-Many association, so that means using proxy methods and relying on a Logic class.
The code
There's a Category entity and a Product entity:
/**
* #ORM\Table(name="category")
* #ORM\Entity(repositoryClass="CategoryRepository")
*/
class Category
{
/**
...
*/
protected $id = null;
/**
* #ORM\OneToMany(targetEntity="CategoryProduct", mappedBy="category", fetch="LAZY", cascade={"persist"})
*/
protected $categoryProducts;
}
and
/**
* #ORM\Table(name="product")
* #ORM\Entity(repositoryClass="ProductRepository")
*/
class Product
{
/**
...
*/
protected $id = null;
/**
* #ORM\OneToMany(targetEntity="CategoryProduct", mappedBy="product", fetch="LAZY", cascade={"persist"})
*/
protected $categoryProducts;
}
and of course a join entity:
/**
* #ORM\Table(name="category_product")
* #ORM\Entity(repositoryClass="CategoryProductRepository")
*/
class CategoryProduct
{
/**
...
*/
protected $id = null;
/**
* #ORM\ManyToOne(targetEntity="Category", fetch="EAGER", inversedBy="categoryProducts")
* #ORM\JoinColumn(onDelete="CASCADE")
*/
protected $category;
/**
* #ORM\ManyToOne(targetEntity="Product", fetch="EAGER", inversedBy="categoryProducts")
* #ORM\JoinColumn(onDelete="CASCADE")
*/
protected $product;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
protected $starred = false;
}
The problem
How to keep an up-to-date list of CategoryProduct entities available to both entities in a pure ORM-style way? In an ORM, everything is managed on the Object layer. Changes to DB are made only on user's request, but it's not compulsory as long as one only works from the ORM point of view. In other words:
$category->addProduct($product);
does not write anything to the DB, and does not even persist any object to the entity manager, but one can still retrieve or remove this product from the list as long as the script runs.
In the case of a custom join table, it's different, because when one wants to add a product, he must create and persist a CategoryProduct entity. So what if we need to retrieve this association from the inverse side?. Here is a code sample that demonstrates my problem:
$product->addCategory($category);
$category->addProduct($product);
In this bidirectional association, how can the $category::addProduct function know about the instance of CategoryProduct entity created by $product::addcategory? The risk is to create two similar join entities for the same association, and i don't know how to avoid it.

Foreign key mapping in Doctrine 2

I have a table user with id as the primary key and user_type_id as the foreign key which is the primary key to table user_type.
Can someone spot the error in the mapping i have used in User entity?
/**
* Primary Identifier
* #OneToMany(targetEntity="user_type")
* #JoinColumn(name="user_type_id", referencedColumnName="id")
* #ORM\Column(name="user_type_id", type="integer")
* #var integer
* #access protected
*/
protected $userTypeId;
Also Should i give mapping in user_type table.
There appear to be quite a lot of errors actually. Firstly: in ZF2 all Annotations are required to be prepended by #ORM\ so #OneToMany => #ORM\OneToMany and #JoinColumn => #ORM\JoinColumn. The #ORM\Column-Part isn't even needed in this example, as this will automatically be defined by #ORM\JoinColumn
To find out more about precise errors, use the Doctrine-CLI-Tool to validate your Annotation-Schemata
./vendor/bin/doctrine-module orm:validate-schema

How to deal with association on composite key entities with Doctrine2?

Say I have an Offer, which can have 1-n Range.
Immediately you think, "put a offer_id inside Range".
But my Offer has a composite primary key (composed of two fields). There is no AUTOINCREMENT id column.
The Doctrine2 documentation doesn't say much about that particular case, here is my entities:
<?php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table()
* #ORM\Entity
*/
class Offer
{
/**
* #var Site $site
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Site")
* #ORM\JoinColumn(name="site_id", referencedColumnName="id")
*/
private $site;
/**
* #var string $pouet
* #ORM\Id
* #ORM\Column(name="pouet", type="string", length=255)
*/
private $pouet;
}
<?php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="RangeItem")
* #ORM\Entity
*/
class Range
{
/**
* #todo This is test code only do not push me :-)
* #var ArrayCollection
* #ORM\ManyToOne(targetEntity="Offer")
*/
private $offers;
}
I obtaind this error:
[Doctrine\ORM\ORMException]
Column name id referenced for relation from
Pouet\MyBundle\Entity\Range towards Pouet\MyBundle\Entity\Offer does
not exist.
That make sense, but how can I deal with this issue? Is a Table with composite primary key forbidden to have associations on it?
I believe the solution is to mirror the primary key (PK) for the foreign key (FK). I.E. for each column that makes up the PK (site, pouet) you need to have the same columns on the related entity.
You can do this by using the JoinColumns annotation (or the equivalent in YAML/XML) with a JoinColumn for each part of the composite FK:
/**
* #ORM\Table(name="RangeItem")
* #ORM\Entity
*/
class Range
{
/**
* #todo This is test code only do not push me :-)
* #var ArrayCollection
* #ORM\ManyToOne(targetEntity="Offer")
* #ORM\JoinColumns(
* #ORM\JoinColumn(name="site_id", referencedColumnName="site_id"),
* #ORM\JoinColumn(name="pouet", referencedColumnName="pouet")
* )
*/
private $offers;
}
I hope this might help somebody who is still struggling with this issue.
You should be able to use a #JoinColumn annotation in the Range class to specify which Id to use:
/**
* #ORM\ManyToOne(targetEntity="Offer")
* #ORM\JoinColumn(name="offer_pouet", referencedColumnName="pouet")
*/
private $offers;
Because the defaults for #JoinColumn, if you do not specify them, would be offer_id and id, respectively, you need to manually specify (I'm making a bit of an assumption here that pouet is a unique value for your Offer class).
EDIT: based on your comment, I found a tutorial on the Doctrine Project site for Composite Primary Key. The entity relationship has mappedBy for one key and indexBy for the other. Hope that helps.