This example shows us how we create many-to-many relationship with Doctrine and Symfony.


Notes



Design




Mapping


Competition


declare(strict_types=1);

namespace AppBundle\Entity;

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

/**
* @ORM\Entity
* @ORM\Table(name="competition")
*/
class Competition
{
/**
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

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

/**
* @ORM\ManyToMany(targetEntity="Team", mappedBy="competitions")
*/
private $teams;

public function __construct()
{
$this->teams = new ArrayCollection();
}

public function getId(): int
{
return $this->id;
}

public function setName(string $name): self
{
$this->name = $name;

return $this;
}

public function getName(): string
{
return $this->name;
}

public function addTeam(Team $team): self
{
$this->teams[] = $team;

return $this;
}

public function removeTeam(Team $team): bool
{
return $this->teams->removeElement($team);
}

public function getTeams(): Collection
{
return $this->teams;
}
}

Team


declare(strict_types=1);

namespace App\Entity;

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

/**
* @ORM\Entity
* @ORM\Table(name="team")
*/
class Team
{
/**
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

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

/**
* @ORM\ManyToMany(targetEntity="Competition", inversedBy="teams", cascade={"persist"})
* @ORM\JoinTable(
* name="team_competition",
* joinColumns={
* @ORM\JoinColumn(name="team_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* @ORM\JoinColumn(name="competition_id", referencedColumnName="id")
* }
* )
*/
private $competitions;

public function __construct()
{
$this->competitions = new ArrayCollection();
}

public function getId(): int
{
return $this->id;
}

public function setName(string $name): self
{
$this->name = $name;

return $this;
}

public function getName(): string
{
return $this->name;
}

public function addCompetition(Competition $competition): self
{
$this->competitions[] = $competition;

return $this;
}

public function removeCompetition(Competition $competition): bool
{
return $this->competitions->removeElement($competition);
}

public function getCompetitions(): Collection
{
return $this->competitions;
}
}

Database


CREATE TABLE `competition` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE `team` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE `team_competition` (
`team_id` int(11) NOT NULL,
`competition_id` int(11) NOT NULL,
PRIMARY KEY (`team_id`,`competition_id`),
KEY `IDX_5E3AC3FC296CD8AE` (`team_id`),
KEY `IDX_5E3AC3FC7B39D312` (`competition_id`),
CONSTRAINT `FK_5E3AC3FC296CD8AE` FOREIGN KEY (`team_id`) REFERENCES `team` (`id`),
CONSTRAINT `FK_5E3AC3FC7B39D312` FOREIGN KEY (`competition_id`) REFERENCES `competition` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Tests


Create a new Competition.


$competition = new Competition();
$competition->setName('Champions League');

$this->entityManager->persist($competition);
$this->entityManager->flush();

Create a new Team.


$team = new Team();
$team->setName('Fenerbahce');

$this->entityManager->persist($team);
$this->entityManager->flush();

Link an existing Competition to a new Team.


/** @var Competition $competition */
$competition = $this->entityManager->getRepository(Competition::class)->findOneById(1);

$team = new Team();
$team->setName('Arsenal');
$team->addCompetition($competition);

// You can use both of them at same time but using one over another is more logical as cascade={"persist"} will handle it for us.
$competition->addTeam($team);
$this->entityManager->persist($team);
//

$this->entityManager->flush();

Link an existing Team to an exiting Competition.


/** @var Team $team */
$team = $this->entityManager->getRepository(Team::class)->findOneById(2);

$competition = new Competition();
$competition->setName('Euro League 3');
$competition->addTeam($team);

$team->addCompetition($competition);

// This will not cause any problem so is optional as cascade={"persist"} will handle it for us.
$this->entityManager->persist($competition);
//

$this->entityManager->flush();