Search code examples
phpormdoctrine-ormmany-to-manyself-reference

How to create many-to-many self-referencing association with extra fields by using Doctrine 2?


The idea is as follows: there are products simple and compound. Compound products may consist of few simple products, for example:

There is product "Cocktail" - a simple product, with its own characteristics (name, description, price etc.), and there is a compound product - the "Fountain of cocktails," which includes the product "Cocktail" as a main component. One "Fountain of cocktails" consists of 50 "Cocktail".

Right now I have the Product entity which has many-to-many relationship with self-referencing:

<?php

namespace CT\AppBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * Class Product
 *
 * @ORM\Entity
 * @ORM\Table(name="products")
 */
class Product
{
    /**
     * @ORM\id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     *
     * @var int
     */
    protected $id;

    /**
     * @ORM\Column(type="string")
     *
     * @var string
     */
    protected $name;

    /**
     * @ORM\Column(
     *     name="description",
     *     type="text"
     * )
     *
     * @var string
     */
    protected $desc;

    /**
     * @ORM\Column(type="float")
     *
     * @var float
     */
    protected $price;

    /**
     * @ORM\Column(
     *     name="count_type",
     *     type="string"
     * )
     *
     * @var string
     */
    protected $countType;

    /**
     * @ORM\Column(
     *     name="default_count",
     *     type="integer",
     *     options={"unsigned": true}
     * )
     *
     * @var int
     */
    protected $defCount;

    /**
     * @ORM\Column(type="boolean")
     *
     * @var bool
     */
    protected $isCountable;

    /**
     * @ORM\Column(type="boolean")
     *
     * @var bool
     */
    protected $isCompound;

    /**
     * @ORM\ManyToMany(
     *     targetEntity="Product",
     *     mappedBy="components"
     * )
     *
     * @var ArrayCollection
     */
    private $products;

    /**
     * @ORM\ManyToMany(
     *     targetEntity="Product",
     *     inversedBy="products"
     * )
     * @ORM\JoinTable(
     *     name="compound_products",
     *     joinColumns={
     *         @ORM\JoinColumn(
     *             name="main_product_id",
     *             referencedColumnName="id"
     *         )
     *     },
     *     inverseJoinColumns={
     *         @ORM\JoinColumn(
     *             name="component_product_id",
     *             referencedColumnName="id"
     *         )
     *     }
     * )
     *
     * @var ArrayCollection
     */
    private $components;

    /**
     * Product constructor.
     */
    public function __construct()
    {
        $this->products = new ArrayCollection();
        $this->components = new ArrayCollection();
    }

    // Getters & setters...
}

I want to add extra field amount to compound_products table for setting the amount of simple products in one compound product, to get an output: http://prntscr.com/cgdvc3

I found one solution, but I don't quite understand how to apply it to my problem: Doctrine many to many self referencing with extra columns

Could you explain me how could I add this extra field for my task with saving the relationship many-to-many with self-referencing? Or if you has better solution could you share it?


Solution

  • 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;