Doctrine Symfony ManyToMany example
Table of Contents
Introduction
In this post, I will show you how to use the ManyToMany
relationship in Symfony with Doctrine. I will use my own code from 1payment.tools SaaS project.
Suppose we have two entities: Tool
and Category
. Each tool can be in multiple categories, and each category can have multiple tools.
Classes
TIP
You should use the bin/console make:entity
command to generate the classes.
Tool
class:
src/Entity/Tool.php
<?php
namespace App\Entity;
use App\Repository\ToolRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ToolRepository::class)]
class Tool
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 155)]
private ?string $name = null;
/**
* @var Collection<int, Category>
*/
#[ORM\ManyToMany(targetEntity: Category::class, inversedBy: 'tools', fetch: 'EAGER')]
private Collection $category;
public function __construct()
{
$this->category = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): static
{
$this->name = $name;
return $this;
}
/**
* @return Collection<int, Category>
*/
public function getCategory(): Collection
{
return $this->category;
}
public function addCategory(Category $category): static
{
if (!$this->category->contains($category)) {
$this->category->add($category);
}
return $this;
}
public function removeCategory(Category $category): static
{
$this->category->removeElement($category);
return $this;
}
}
Category
class:
src/Entity/Category.php
<?php
namespace App\Entity;
use App\Repository\CategoryRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: CategoryRepository::class)]
class Category
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 155)]
private ?string $name = null;
#[ORM\Column(length: 255)]
private ?string $slug = null;
/**
* @var Collection<int, Tool>
*/
#[ORM\ManyToMany(targetEntity: Tool::class, mappedBy: 'category')]
private Collection $tools;
public function __construct()
{
$this->tools = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): static
{
$this->name = $name;
return $this;
}
public function getSlug(): ?string
{
return $this->slug;
}
public function setSlug(string $slug): static
{
$this->slug = $slug;
return $this;
}
/**
* @return Collection<int, Tool>
*/
public function getTools(): Collection
{
return $this->tools;
}
public function addTool(Tool $tool): static
{
if (!$this->tools->contains($tool)) {
$this->tools->add($tool);
$tool->addCategory($this);
}
return $this;
}
public function removeTool(Tool $tool): static
{
if ($this->tools->removeElement($tool)) {
$tool->removeCategory($this);
}
return $this;
}
}
Doctrine QueryBuilder example:
Get all tools what have any categories:
$this->em->getRepository(Tool::class)->createQueryBuilder('t')
->join('t.category', 'c')
->getQuery()
->getResult();
Get all tools not depends on category presents
$this->em->getRepository(Tool::class)->createQueryBuilder('t')
->leftJoin('t.category', 'c')
->getQuery()
->getResult();
Get all Tools with zero categories:
$this->em->getRepository(Tool::class)->createQueryBuilder('t')
->leftJoin('t.category', 'c')
->where('c.id IS NULL')
->getQuery()
->getResult();
Get all Tools with specific category:
$this->em->getRepository(Tool::class)->createQueryBuilder('t')
->join('t.category', 'c')
->where('c.name LIKE :name')
->setParameter('name', '%Portfolio%')
->getQuery()
->getResult();