How to implement many-to-many with extra fields in symfony2?
Once an association has data, it's no more an association.
You have to implement two ManyToOne
instead of a ManyToMany
.
See this great answer on this question for a full example.
You can get a lot of other examples by googling the title of your question.
Doctrine 2 and Many-to-many link table with an extra field
A Many-To-Many association with additional values is not a Many-To-Many, but is indeed a new entity, since it now has an identifier (the two relations to the connected entities) and values.
That's also the reason why Many-To-Many associations are so rare: you tend to store additional properties in them, such as sorting
, amount
, etc.
What you probably need is something like following (I made both relations bidirectional, consider making at least one of them uni-directional):
Product:
namespace Entity;
use Doctrine\ORM\Mapping as ORM;
/** @ORM\Table(name="product") @ORM\Entity() */
class Product
{
/** @ORM\Id() @ORM\Column(type="integer") */
protected $id;
/** ORM\Column(name="product_name", type="string", length=50, nullable=false) */
protected $name;
/** @ORM\OneToMany(targetEntity="Entity\Stock", mappedBy="product") */
protected $stockProducts;
}
Store:
namespace Entity;
use Doctrine\ORM\Mapping as ORM;
/** @ORM\Table(name="store") @ORM\Entity() */
class Store
{
/** @ORM\Id() @ORM\Column(type="integer") */
protected $id;
/** ORM\Column(name="store_name", type="string", length=50, nullable=false) */
protected $name;
/** @ORM\OneToMany(targetEntity="Entity\Stock", mappedBy="store") */
protected $stockProducts;
}
Stock:
namespace Entity;
use Doctrine\ORM\Mapping as ORM;
/** @ORM\Table(name="stock") @ORM\Entity() */
class Stock
{
/** ORM\Column(type="integer") */
protected $amount;
/**
* @ORM\Id()
* @ORM\ManyToOne(targetEntity="Entity\Store", inversedBy="stockProducts")
* @ORM\JoinColumn(name="store_id", referencedColumnName="id", nullable=false)
*/
protected $store;
/**
* @ORM\Id()
* @ORM\ManyToOne(targetEntity="Entity\Product", inversedBy="stockProducts")
* @ORM\JoinColumn(name="product_id", referencedColumnName="id", nullable=false)
*/
protected $product;
}
Doctrine2: Best way to handle many-to-many with extra columns in reference table
I've opened a similar question in the Doctrine user mailing list and got a really simple answer;
consider the many to many relation as an entity itself, and then you realize you have 3 objects, linked between them with a one-to-many and many-to-one relation.
http://groups.google.com/group/doctrine-user/browse_thread/thread/d1d87c96052e76f7/436b896e83c10868#436b896e83c10868
Once a relation has data, it's no more a relation !
Additional fields in ORM-ManyToMany relation
The simpliest way to do this that I've found is to use a third entity
Here's how it would work in your case
User entity :
/**
* @ORM\Entity
* @ORM\Table(name="users")
*/
class User extends BaseUser
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\OneToMany(targetEntity="UserGroup", mappedBy="user")
* */
protected $usergroup;
}
Group Entity :
/**
* @ORM\Entity
* @ORM\Table(name="groups")
*/
class Group
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\OneToMany(targetEntity="UserGroup" , mappedBy="group"})
* */
protected $usergroup;
}
UserGroup Entity:
/**
* @ORM\Entity
* @ORM\Table(name="user_group")
*/
class UserGroup
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*
* @var integer $id
*/
protected $id;
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="usergroup")
* @ORM\JoinColumn(name="user_id", referencedColumnName="id")
* */
protected $user;
/**
* @ORM\ManyToOne(targetEntity="Group", inversedBy="usergroup")
* @ORM\JoinColumn(name="group_id", referencedColumnName="id")
* */
protected $group;
// Additional fields, Getter, Setters, _Construct, __toString
}
Doctrine 2: How to handle join tables with extra columns
First off, let me explain that this does not exist:
A join table (also known as a junction table or cross-reference table) is a table that links 2 (or more) other tables together within the same database by primary key.
This means that a join table will only contain foreign keys, there is no place for these extra columns.
So when you need extra columns in such a table, it is no longer just a "link" between other tables, but becomes a real table on its own!
In terms of Doctrine 2, you no longer have a many-to-many association between 2 entities, but get a one-to-many/many-to-one association between 3 entities.
Continue reading here for more detailed explanations:
- Doctrine 2: How to handle join tables with extra columns
- More on one-to-many/many-to-one associations in Doctrine 2
Symfony: ManyToMany table extra columns
You need to break your ManyToMany
relation to OneToMany
and ManyToOne
by introducing a junction entity called as UserHasHouses
, This way you could add multiple columns to your junction table user_house
User Entity
/**
* User
* @ORM\Table(name="user")
* @ORM\Entity
*/
class User
{
/**
* @ORM\OneToMany(targetEntity="NameSpace\YourBundle\Entity\UserHasHouses", mappedBy="users",cascade={"persist","remove"} )
*/
protected $hasHouses;
}
House Entity
/**
* Group
* @ORM\Table(name="house")
* @ORM\Entity
*/
class House
{
/**
* @ORM\OneToMany(targetEntity="NameSpace\YourBundle\Entity\UserHasHouses", mappedBy="houses",cascade={"persist","remove"} )
*/
protected $hasUsers;
}
UserHasHouses Entity
/**
* UserHasHouses
* @ORM\Table(name="user_house")
* @ORM\Entity
*/
class UserHasHouses
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="NameSpace\YourBundle\Entity\House", cascade={"persist"}, fetch="LAZY")
* @ORM\JoinColumn(name="house_id", referencedColumnName="id")
*/
protected $houses;
/**
* @ORM\ManyToOne(targetEntity="NameSpace\YourBundle\Entity\User", cascade={"persist","remove"}, fetch="LAZY" )
* @ORM\JoinColumn(name="user_id", referencedColumnName="id",nullable=true)
*/
protected $users;
/**
* @var \DateTime
* @ORM\Column(name="created_at", type="datetime")
*/
protected $createdAt;
/**
* @var \DateTime
* @ORM\Column(name="updated_at", type="datetime")
*/
protected $updatedAt;
//... add other properties
public function __construct()
{
$this->createdAt= new \DateTime('now');
}
}
have additional column in ManyToMany join table in Doctrine (Symfony2)
Laravel doctrine2 many to many relation with extra column
As per my solution which has worked, by reading another question passed by @Nicola Havric - Read as follow That doctrine does not support extra columns in a many-to-many relation. Thus you should use the relation as it's own entity. My own solution was to change the way I wanted it to run with flushing.
In my controller I changed my code as follow:
try {
$profile = new Profile(
$request->input('company_id'),
$request->input('name'),
$request->input('lastname'),
$request->input('gender'),
new DateTime(),
$time,
$time
);
$company = $this->em->getRepository(Company::class)->find($request->input('company_id'));
$profile->addCompany($company);
//Flush the user, so I can grab it's profile ID
$this->em->persist($profile);
$this->em->flush();
foreach($request->input('profile_skills') as $skill => $level) {
$skill = $this->em->getRepository(Skill::class)->find($skill);
$skill->level = $level;
$profile->addSkill($skill);
}
$this->em->getConnection()->commit();
Inside my Profile Entity function:
public function addSkill(Skill $skill)
{
//I left this check since it only checks if the relation is set already. If so, it will skip it.
if ($this->skills->contains($skill)) {
return;
}
//Since this function gets called inside a loop, I can call the entity to add a new "relation" to the table.
(new ProfileHasSkill($this->getId(), $skill, $skill->level))->addSkill($this->getId(), $skill, $skill->level);
return true;
}
Inside my ProfileHasSkill entity:
public function addSkill($profileId, $skill)
{
//Creating a new ProfileHasSkill inside the table.
$profileSkill = new ProfileHasSkill(
$profileId,
$skill->getId(),
$skill->level
);
/*Since I do a roll-back inside my controller in case something goes wrong.
I decided to add the flush here.
As far no additional checks where needed in my case
since I require a Profile instance and a Skill instance inside the Profile entity.*/
EntityManager::persist($profileSkill);
EntityManager::flush();
}
How to create many-to-many self-referencing association with extra fields by using Doctrine 2?
You need to create a separate entity to link your Entities. Something like ProductCompound.
And then link it twice to your Product entity, for each relation.
/**
* Class ProductCompound
*
* @ORM\Entity
* @ORM\Table(name="compound_products")
*/
class ProductCompound
{
/**
* @ORM\id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*
* @var int
*/
protected $id;
/**
* @ORM\ManyToOne(
* targetEntity="Product",
* inversedBy="products"
* )
* @ORM\JoinColumn(name="main_product_id", referencedColumnName="id"
*
* @var ArrayCollection
*/
protected $mainProduct;
/**
* @ORM\ManyToOne(
* targetEntity="Product",
* inversedBy="components"
* )
* @ORM\JoinColumn(name="component_product_id", referencedColumnName="id"
*
* @var ArrayCollection
*/
protected $componentProduct;
/**
* @var double
*
* @ORM\Column(name="amount", type="float", nullable=true)
*/
protected $amount;
And modify Product:
/**
* Class Product
*
* @ORM\Entity
* @ORM\Table(name="products")
*/
class Product
{
...
/**
* @ORM\OneToMany(
* targetEntity="ProductCompound",
* mappedBy="mainProduct"
* )
*
* @var ArrayCollection
*/
private $productLinks;
/**
* @ORM\OneToMany(
* targetEntity="ProductCompound",
* mappedBy="componentProduct"
* )
*
* @var ArrayCollection
*/
private $componentLinks;
Related Topics
Laravel Certificate Verification Errors When Sending Tls Email
Sanitizing Strings to Make Them Url and Filename Safe
Upgrading PHP in Xampp For Windows
How to Create Virtual Host on Xampp
In Where Shall I Use Isset() and !Empty()
Serializing PHP Object to Json
How to Extract Text from the Pdf Document
Use PHP to Set Cron Jobs in Windows
Export MySQL Data to Excel in PHP
How to Remove Extension from String (Only Real Extension!)
How to Get Rid of Eval-Base64_Decode Like PHP Virus Files
PHP Curl, Extract an Xml Response
How to Check If a Word Is Contained in Another String Using PHP
Passing Multiple Variables to Another Page in Url
Does MySQL_Real_Escape_String() Fully Protect Against SQL Injection