18/02/2017 - DOCTRINE, SYMFONY
This example shows us how we create one-to-one 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 test below."remove"
option to cascade
attribute on the both entities otherwise removing one record will not remove record associated to it.declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="continent")
*/
class Continent
{
/**
* @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\OneToOne(targetEntity="Country", mappedBy="continent", cascade={"persist", "remove"})
*/
private $country;
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 getCountry(): Country
{
return $this->country;
}
public function setCountry(Country $country): self
{
$this->country = $country;
return $this;
}
}
declare(strict_types=1);
namespace App\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=100, nullable=false)
*/
private $name;
/**
* @ORM\OneToOne(targetEntity="Continent", inversedBy="country", cascade={"persist", "remove"})
* @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 getContinent(): Continent
{
return $this->continent;
}
public function setContinent(Continent $continent): self
{
$this->continent = $continent;
return $this;
}
}
CREATE TABLE `continent` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `country` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`continent_id` int(11) NOT NULL,
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_5373C966921F4C77` (`continent_id`),
UNIQUE KEY `UNIQ_5373C9665E237E06` (`name`),
CONSTRAINT `FK_5373C966921F4C77` FOREIGN KEY (`continent_id`) REFERENCES `continent` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Creating a new Continent and a new Country at same time.
$continent = new Continent();
$continent->setName('Africa');
$country = new Country();
$country->setName('Gana');
$country->setContinent($continent);
$continent->setCountry($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();