18/02/2017 - DOCTRINE, SYMFONY
This example shows us how we create one-to-many composition relationship with Doctrine and Symfony.
inversedBy
attribute.mappedBy
attribute.nullable=false
attribute on "owning" side enforces the composition relationship.cascade={"persist"}
attribute on both entities so this is a precautionary measure. It will also help us not calling persist
on a new child object where possible. You will see in the tests below."remove"
option to cascade
attribute on the parent entity which is "Continent" otherwise trying to remove a parent record would throw an exception if there is a child record associated to it.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="continent", uniqueConstraints={@ORM\UniqueConstraint(columns={"name"})})
*/
class Continent
{
/**
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(name="name", type="string", length=13, nullable=false)
*/
private $name;
/**
* @ORM\OneToMany(targetEntity="Country", mappedBy="continent", cascade={"persist", "remove"})
*/
private $countries;
public function __construct()
{
$this->countries = 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 addCountry(Country $country): self
{
$this->countries[] = $country;
return $this;
}
public function removeCountry(Country $country): bool
{
return $this->countries->removeElement($country);
}
public function getCountries(): Collection
{
return $this->countries;
}
}
declare(strict_types=1);
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="country", uniqueConstraints={@ORM\UniqueConstraint(columns={"name"})})
*/
class Country
{
/**
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(name="name", type="string", length=13, nullable=false)
*/
private $name;
/**
* @ORM\ManyToOne(targetEntity="Continent", inversedBy="countries", cascade={"persist"})
* @ORM\JoinColumn(name="continent_id", referencedColumnName="id", nullable=false)
*/
private $continent;
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 setContinent(Continent $continent): self
{
$this->continent = $continent;
return $this;
}
public function getContinent(): Continent
{
return $this->continent;
}
}
CREATE TABLE `continent` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(13) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_6CC70C7C5E237E06` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `country` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`continent_id` int(11) NOT NULL,
`name` varchar(13) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_5373C9665E237E06` (`name`),
KEY `IDX_5373C966921F4C77` (`continent_id`),
CONSTRAINT `FK_5373C966921F4C77` FOREIGN KEY (`continent_id`) REFERENCES `continent` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Create a new Continent.
$continent = new Continent();
$continent->setName('Africa');
$this->entityManager->persist($continent);
$this->entityManager->flush();
Create a new Continent and Country at same time.
$country = new Country();
$country->setName('Gana');
$continent = new Continent();
$continent->setName('Africa');
$country->setContinent($continent);
$continent->addCountry($country);
// 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.
$this->entityManager->persist($continent);
$this->entityManager->persist($country);
//
$this->entityManager->flush();
Creating a new Country.
/** @var Continent $continent */
$continent = $this->entityManager->getRepository(Continent::class)->findOneById(1);
$country = new Country();
$country->setName('Gana');
$country->setContinent($continent);
// 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.
$continent->addCountry($country);
$this->entityManager->persist($country);
//
$this->entityManager->flush();