Doctrine multiple composite foreign key - php

I am trying to construct an object with two composite foreign keys pointing out to the same object, but they seem to have the same data, like doing the join only on one column, product_id.
class PostpaidProduct extends Product {
/**
* #ManyToOne(targetEntity="Bundle", fetch="EAGER", cascade={"persist"})
* #JoinColumn(name="bundle_voice_id", referencedColumnName="id")
*/
private $bundleVoice;
/**
* #ManyToOne(targetEntity="Bundle", fetch="EAGER", cascade={"persist"})
* #JoinColumn(name="bundle_data_id", referencedColumnName="id")
*/
private $bundleData;
/**
* #OneToMany(targetEntity="BundlePromo", mappedBy="product", fetch="EAGER", cascade={"persist"})
* #JoinColumns({
* #JoinColumn(name="id", referencedColumnName="product_id"),
* #JoinColumn(name="bundle_voice_id", referencedColumnName="bundle_id")
* })
*/
private $bundleVoicePromos;
/**
* #OneToMany(targetEntity="BundlePromo", mappedBy="product", fetch="EAGER", cascade={"persist"})
* #JoinColumns({
* #JoinColumn(name="id", referencedColumnName="product_id"),
* #JoinColumn(name="bundle_data_id", referencedColumnName="bundle_id")
* })
*/
private $bundleDataPromos;
}
What would be wrong with my mapping?
Is it possible to have composite foreign keys but without being primary keys?

I have talked to one of the developers of Doctrine and he said that the #JoinColumns field in #OneToMany relationships is ignored. The alternative would be having just one foreign key and to use matching criterias in an entity method, filtering for the entries needed based on the other key. Another solution would be having repository methods specific for getting these values.
Also, in OneToMany relationships eager fetching does not work, so it does separate queries for all children. So if you have a product with multiple prices, when fetching a product it will do separate queries for fetching the prices.

Related

How to OrderBy in Many to Many Relationship in Symfony

I have two tables products and categories. Products can have many categories and categories can have many products.(The relationship between these two tables is many-to-many)
To map the associations between these two tables I have created another new table product_categories which stores product_id and category_id.
Following is my annotations used in entities to join these three tables.
Product Entity
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Category", mappedBy="products")
*/
private $categories;
Category Entity
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Product", inversedBy="categories")
* #ORM\JoinTable(name="product_categories",
* joinColumns={
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
* }
* )
*/
private $products;
Now the problem is, I want to order the records in product_categories into a certain order. So I added a new column called serial_number to product_categories table.
The result what I am trying to achieve is when I am retrieving categories to a product using $product->getCategories() the resulting category array should be ordered by the serial_number.
I have tried the following annotations.
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Product", inversedBy="categories")
* #ORM\JoinTable(name="product_categories",
* joinColumns={
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
* }
* )
* #OrderBy({"product_categories.serial_number"="DESC"})
*/
private $products;
But this results in an annotation syntax error.
Fatal error: Uncaught Doctrine\Common\Annotations\AnnotationException: [Semantical Error] The annotation "#OrderBy" in property ProductBundle\Entity\Product::$products was never imported. Did you maybe forget to add a "use" statement for this annotation? in /var/www/sonicwall.local/app/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php on line 54
When I use #ORM\OrderBy({"product_categories.serial_number"="DESC"}), I do not get a syntax error but results are not getting ordered either.
My question is, is there a way to achieve this using annotations without me having to create new Entities and Repositories for product_categories table?
You have to use #ORM\OrderBy:
#OrderBy acts as an implicit ORDER BY clause for the given fields, that is appended to all the explicitly given ORDER BY items.
All collections of the ordered type are always retrieved in an ordered fashion.
To keep the database impact low, these implicit ORDER BY items are only added to a DQL Query if the collection is fetch joined in the DQL
query.
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Product", inversedBy="categories")
* #ORM\JoinTable(name="product_categories",
* joinColumns={
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
* }
* )
* #ORM\OrderBy({"serialNumber"="DESC"})
*/
private $products;
You tried to do something similar, but with product_categories.serial_number. You do not need product_categories because the order by will try to sort every member of the collection with its own field. So just writing serialNumber will sort by each product serial number.
However, I just saw that you already tried something similar in comments so makes sure to use the attribute name as your field: serialNumber, serial, ... as it appear with $serialNumber

Symfony2 Get entities of an entity in another entity

This question is about Symfony2 table relationships using ORM. I have three tables/entities that are related to each other. The relationship is very similar to Wordpress Posts, Categories and Categories relationship tables.
Table 1 contains posts.
Table 2 contains categories
Table 3 contains relationships between the categories and posts.
I want to be able to have the categories property in the posts table and a posts property in the categories table. So that when I call.
Categories->posts : I should get posts in that category.
Posts->categories : I should get the categories the post belongs to.
I want to have unique categories per table and I want all posts to point to a category without having to create a new entry for the category that already exists which is what ManyToOne or OneToMany is offering this is why the third table I think is necessary.
For example here is the relationships
class Category_relationship
{
/**
* #var integer
*
* #ORM\Column(name="object_id", type="bigint")
*
* #ORM\ManyToOne(targetEntity="Worksheet", inversedBy="category_relationships")
* #ORM\JoinColumn(name="worksheet_id", referencedColumnName="id", nullable=FALSE)
*/
private $objectId;
/**
* #var integer
*
* #ORM\Column(name="category_id", type="bigint")
*
* #ORM\ManyToOne(targetEntity="Category", inversedBy="categories")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=FALSE)
*/
private $categoryId;
}
Here is the Category class:
class Category
{
/**
* #ORM\OneToMany(targetEntity="Category_relationship", mappedBy="categoryId", cascade={"persist", "remove"}, orphanRemoval=TRUE)
*/
protected $posts;
}
Here is the Category class:
class Posts
{ /**
* #ORM\OneToMany(targetEntity="Category_relationship", mappedBy="objectId", cascade={"persist", "remove"}, orphanRemoval=TRUE)
*/
protected $categories;
}
I want to create a system where I can assign posts to a category but the category table can only contain 1 entry about the category. I also want to be able to use expressions link;
Post->categories
Category->posts
or
Post->AddCategory()
Category->AddPost()
Thanks for your help.
It seems that you want a simple many-to-many relationship.
Every post can have multiple categories, and every category have list of related posts. Many to many handles pivot table by itself.
So, in Post entity you have to declare relationship that way:
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="posts")
* #ORM\JoinTable(name="PostsCategories",
* joinColumns={#ORM\JoinColumn(name="post_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")}
* )
**/
protected $categories;
Remember about using Doctrine\ORM\Mapping with ORM alias (you don't have to import all subclasses separately):
use Doctrine\ORM\Mapping as ORM;
After that, you need to create a new ArrayCollection in class constructor:
public function __construct()
{
$this->categories = new ArrayCollection();
}
And add proper methods, like addCategory:
public function addCategory(Category $category)
{
$this->categories[] = $category;
return $this;
}
You can also add them automatically with:
php app/console doctrine:generate:entities BundleName:EntityName
Same thing in Category entity, but with a little different definiton:
/**
* #ORM\ManyToMany(targetEntity="Post", mappedBy="categories")
**/
protected $posts;
You can find all of these information in Doctrine docs

More than one ManyToMany realtions - Doctrine, Symfony

I have two tables "RFQ" and "RFQItem".
First ManyToMany relation I need to make a choose which RFQItems add to RFQ in RFQ form.
Second ManyToMany relation I need to make a new RFQItems when creating new RFQ.
I have configured all, but schema update throws me an error:
The table with name 'rfqgroup.rfq_rfqitem' already exists.
Entity:
/**
* #ORM\ManyToMany(targetEntity="RFQItem")
* #ORM\JoinColumn(referencedColumnName="id", nullable=true)
*/
protected $rfqitem;
/**
* #ORM\ManyToMany(targetEntity="RFQItem", cascade={"persist"})
* #ORM\JoinColumn(referencedColumnName="rfq_id", nullable=true)
*/
protected $rfq_item_title;
It is even possible to make these relations?
You must use the #JoinTable attribute to ensure that the Many-to-Many tables have unique names.
/**
* #ORM\ManyToMany(targetEntity="RFQItem")
* #ORM\JoinTable(name="rfq_rfqitem",
* joinColumns={#ORM\JoinColumn(name="rfq_item_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="rfqgroup_id", referencedColumnName="id")}
* )
*/
public $rfqitem;
/**
* #ORM\ManyToMany(targetEntity="RFQItem")
* #ORM\JoinTable(name="rfq_rfqitem_title",
* joinColumns={#ORM\JoinColumn(name="rfq_item_title_id", referencedColumnName="rfq_id")},
* inverseJoinColumns={#ORM\JoinColumn(name="rfqgroup_id", referencedColumnName="id")}
* )
*/
public $rfq_item_title;
You may need to correct the JoinColumn attributes for name and referencedColumnName depending on the variable names you've picked in each entity.

adding created timestamp to join table in doctrine2

I have the following property in my User entity to track followers and following. Basically a user can follow other user as well. I have a join column called app_user_follow_user, however I also wanted to add a timestamp of whenever someone follows another user, when did it happen. How can I specify a created timestamp via this ORM?
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="following")
*/
protected $followers;
/**
* #ORM\ManyToMany(targetEntity="User", inversedBy="followers")
* #ORM\JoinTable(name="app_user_follow_user",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="follow_user_id", referencedColumnName="id")}
* )
*/
protected $following;
Doctrine ManyToMany relationships are used when your join table has two columns. If you need to add another column you have to convert the relationship to OneToMany on both sides and ManyToOne on the joined entity.
This is entirely untested but it will hopefully give you the gist.
User Entity
/**
* #ORM\OneToMany(targetEntity="AppUserFollowUser", mappedBy="appUser")
*/
protected $followers;
/**
* #ORM\OneToMany(targetEntity="AppUserFollowUser", mappedBy="followUser")
*/
protected $following;
AppUserFollowUser Entity
/**
* #ORM\Table(name = "app_user_follow_user")
*/
class AppUserFollowUser
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="User", inversedBy="followers")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* })
*/
private $appUser;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="User", inversedBy="following")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="follow_user_id", referencedColumnName="id")
* })
*/
private $followUser;
/**
* #ORM\Column(name="created_date", type="datetime", nullable=false)
*/
private $createdDate;
}
I think that you will have to create a link entity manually (entiy1 onetomany linkEntity manytoone entity2.
Because, the usual link entity are automated and should be as simple and (data less) as possible, so doctrine can take all the controle over it,
imagine you need to get the timestamp, how can you do it on an (none hard coded) entity, you will need a getter, and the annotations are not supposed to contains code.

Doctrine 2.1 reference table

I have a reference table album_content which has: album_id, content_id, and sort_key. I set it up as an entity with #ManyToOne relations:
/**
* #ManyToOne(targetEntity="Album")
* #JoinColumns({
* #JoinColumn(name="album_id", referencedColumnName="id")
* })
*/
private $albumId;
/**
* #ManyToOne(targetEntity="Content")
* #JoinColumns({
* #JoinColumn(name="content_id", referencedColumnName="id")
* })
*/
private $contentId;
/**
* #Column(name="sort_key", type="integer", nullable=false)
*/
private $sortKey;
Right now Doctrine is complaining No identifier/primary key specified. What's the correct annotation to reference these without adding an extra ID column?
First, you probably shouldn't be naming things $contentId or $albumId, but instead just call them $content and $album.
That said, the quick solution is to add #Id annotations to both of your associations.
The manual goes into further detail about using composite keys in Doctrine 2.

Categories