When you are using Symfony PHP Framework, the most common way to interact with Databases is using Doctrine. And if you are using a relational Database like MySql or MariaDb, you will need to handle Table Associations. One of the most common ones is the Many to Many association, that means "Many of these, can have Many of those".

You may refer to this posts:

 

Lets assume we have two entities:

  • Animals
  • Food

These entities are very simple, only contain the primary key and a column with the name. The many to many association requires a Join Table where the primary keys of each table will be associated.

With these association each Animal can have many different Food so does each Food can have many different Animals.

Steps to create this Many to Many Association.

Step 1. Open a ssh terminal and go to the root of your Symfony project.

 

Step 2.  Assuming you already have the symfony_db schema with the tables on it:

php bin/console doctrine:mapping:import "App\Entity" annotation --path=src/Entity

 

UPDATE on May-2023: I just found today that this doctrine:mapping:import that was deprecated a couple of years ago, may not longer work. Please proceed with caution and review carefully the generated Entity files.

 

That's it! the new Entities and the association are generated automatically by Symfony.

The Animals Entity will look like:

<?php

namespace App\Entity;

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

/**
 * Animals
 *
 * @ORM\Table(name="animals")
 * @ORM\Entity
 */
class Animals
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="animal_name", type="string", length=45, nullable=false)
     */
    private $animalName;

    /**
     * @var Collection
     *
     * @ORM\ManyToMany(targetEntity="Food", inversedBy="animals")
     * @ORM\JoinTable(name="animals_has_food",
     *   joinColumns={
     *     @ORM\JoinColumn(name="animals_id", referencedColumnName="id")
     *   },
     *   inverseJoinColumns={
     *     @ORM\JoinColumn(name="food_id", referencedColumnName="id")
     *   }
     * )
     */
    private $food = array();

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->food = new ArrayCollection();
    }

}

And the Food one:

<?php

namespace App\Entity;

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

/**
 * Food
 *
 * @ORM\Table(name="food")
 * @ORM\Entity
 */
class Food
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="food_name", type="string", length=45, nullable=false)
     */
    private $foodName;

    /**
     * @var Collection
     *
     * @ORM\ManyToMany(targetEntity="Animals", mappedBy="food")
     */
    private $animals = array();

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->animals = new ArrayCollection();
    }

}

 

In this case the Metadata for the joining table is kept in the Animals Entity.

From here you can go and generate your Getters and Setters:

$ php bin/console make:entity --regenerate

Now the Animals Entity will look like:

<?php

namespace App\Entity;

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

/**
 * Animals
 *
 * @ORM\Table(name="animals")
 * @ORM\Entity
 */
class Animals
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="animal_name", type="string", length=45, nullable=false)
     */
    private $animalName;

    /**
     * @var Collection
     *
     * @ORM\ManyToMany(targetEntity="Food", inversedBy="animals")
     * @ORM\JoinTable(name="animals_has_food",
     *   joinColumns={
     *     @ORM\JoinColumn(name="animals_id", referencedColumnName="id")
     *   },
     *   inverseJoinColumns={
     *     @ORM\JoinColumn(name="food_id", referencedColumnName="id")
     *   }
     * )
     */
    private $food = array();

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->food = new ArrayCollection();
    }
    
    /**
     * @return int|null
     */
    public function getId(): ?int
    {
        return $this->id;
    }
    
    /**
     * @return string|null
     */
    public function getAnimalName(): ?string
    {
        return $this->animalName;
    }
    
    /**
     * @param string $animalName
     * @return $this
     */
    public function setAnimalName(string $animalName): self
    {
        $this->animalName = $animalName;

        return $this;
    }

    /**
     * @return Collection<int, Food>
     */
    public function getFood(): Collection
    {
        return $this->food;
    }
    
    /**
     * @param Food $food
     * @return $this
     */
    public function addFood(Food $food): self
    {
        if (!$this->food->contains($food)) {
            $this->food->add($food);
        }

        return $this;
    }
    
    /**
     * @param Food $food
     * @return $this
     */
    public function removeFood(Food $food): self
    {
        $this->food->removeElement($food);

        return $this;
    }

}