Intereting Posts

Отношения один к одному на двух таблицах, разделяющих первичный ключ

Я хочу, чтобы две таблицы Person и Address делили свой первичный ключ:

 CREATE TABLE Person (id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY); CREATE TABLE Address (personId INT UNSIGNED NOT NULL PRIMARY KEY); 

Я пытаюсь моделировать идею, что при создании Person вы также создаете для него Address .

Я придумал следующее сопоставление:

 class Person { /** * @Id * @Column(type="integer") * @GeneratedValue */ private $id; /** * @OneToOne(targetEntity="Address", cascade={"all"}) * @JoinColumn(name="id", referencedColumnName="personId") */ private $address; public function __construct() { $this->address = new Address($this); } } class Address { /** * @Id * @OneToOne(targetEntity="Person") * @JoinColumn(name="personId", referencedColumnName="id") */ private $person; public function __construct(Person $person) { $this->person = $person; } } 

Когда я пытаюсь сохранить Person , я ожидаю, что он будет каскадом Address , но я получаю следующее исключение:

Учение \ ORM \ ORMException

Сущность типа Address имеет личность через чужое лицо Person , однако у этого объекта нет самого идентификатора. Вы должны вызвать EntityManager#persist() для связанного объекта и убедиться, что идентификатор был создан, прежде чем пытаться сохранить Address . В случае создания Post Insert ID Generation (например, MySQL Auto-Increment или PostgreSQL SERIAL ) это означает, что вы должны вызывать EntityManager#flush() между обеими персистентными операциями.

Я не могу явно вызвать flush() , потому что я не явно persist() ing Address , вместо этого я ожидаю, что Person будет каскадом persist Address .

Так что, насколько я понимаю, это должно быть задачей Doctrine, чтобы flush() в нужный момент, чтобы получить id .

Эта взаимосвязь «один-к-одному» отлично каскадируется при использовании разных идентификаторов auto_increment как для Person и для Address (но тогда я должен добавить addressId для Person ).

Моя цель – сделать их одинаковыми.

Это возможно?

Related of "Отношения один к одному на двух таблицах, разделяющих первичный ключ"

Как насчет использования Идентичность через внешние сущности в доктрине 2.1?

В вашем сценарии вы можете использовать @ManyToOne вместо @OneToOne .

редактировать

Ассоциация сохраняется, когда вы сохраняете свою сторону. В вашем сопоставлении это Address . Измените свое сопоставление, чтобы у владельца был внешний ключ, и все должно работать нормально.

 /** * @OneToOne(targetEntity="Address", cascade={"all"}, inversedBy="person") * @JoinColumn(name="id", referencedColumnName="personId") */ private $address; 

Другими словами, если Address должен быть владельцем Person тогда вы должны перенести Address на первое место, чтобы сохранить связь. Если вы хотите, чтобы ассоциация сохранялась с Person , тогда вы должны пометить Person , являющуюся стороной-владельцем, и Address который будет обратной стороной:

 /** * @Id * @OneToOne(targetEntity="Person", inversedBy="address") * @JoinColumn(name="personId", referencedColumnName="id") */ private $person; 

Вы можете использовать прослушиватель symfony и удалить стратегию автоматического увеличения из объекта-адреса:

Сначала создайте Listner и создайте свой класс listner, например AdressGenerator.php

 <?php namespace Zodiac\HelperBundle\Listener; use Doctrine\ORM\Event\LifecycleEventArgs; use AAA\XXXBundle\Entity\Person; use AAA\XXXBundle\Entity\Adress; class AdressGenerator { public function prePersist(LifecycleEventArgs $args) { // This function is called just beforethe event Persist // The AdressGeneratorlistner is defined in the config.yml file // Get the entity manager and the entity from le LifecycleEventArgs $em = $args->getEntityManager(); $entity = $args->getEntity(); if ($entity instanceof Person) { //Create the Adress entity $adress = new Adress(); $adress->setId($entity->getId()); $em->persist($adress); $entity->setAdress($adress); $em->persist($entity); $em->flush(); } } } 

Затем добавьте службу слушателя в config.yml:

 services: AdressGenerator.listener: class: AAA\xxxBundle\Listener\AdressGenerator tags: - { name: doctrine.event_listener, event: prePersist } 

Я нашел решение, но не знаю, лучшее ли это (мне это тяжело).

Это не проблема отображения, а то, как мы сохраняем:

Вместо того, чтобы делать:

 $em->persist($person); $em->flush(); 

Мы должны сделать:

 $em->transactional(function($em) use ($person) { $address = $person->getAddress(); $person->setAddress(null); $em->persist($person); $em->flush(); $address->setPerson($person); $em->persist($address); $em->flush(); }); 

Если у кого-то есть более чистое решение, он получит награду :-).