我想要两个表{{1}}和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
,但我得到以下异常:
学说\ ORM \ ORMException
类型
Address
的实体通过外部实体Address
具有身份, 但是这个实体本身没有身份。你必须打电话 对相关实体Person
并确保一个 在尝试保留EntityManager#persist()
之前生成了标识符。 如果是后插入ID生成(例如MySQLAddress
或PostgreSQLAuto-Increment
)这意味着您必须致电SERIAL
在两个持续的操作之间。
我无法明确调用EntityManager#flush()
,因为我没有明确flush()
persist()
,而是期望Address
级联持久Person
}。
据我了解,在适当的时刻Address
获得flush()
应该是Doctrine的工作。
当对id
和auto_increment
使用不同的Person
ID时,这种一对一的关系会很好(但我必须在{Address
添加addressId
1}})。
我的目标是让他们分享Person
。
这可能吗?
答案 0 :(得分:3)
如何在学说2.1中使用Identity through foreign Entities?
在您的方案中,您可以使用@ManyToOne
代替@OneToOne
关联。
当您坚持拥有方时,该关联会持续存在。在您的映射中,即Address
。更改映射,以便所有者拥有外键,一切都应该正常工作。
/**
* @OneToOne(targetEntity="Address", cascade={"all"}, inversedBy="person")
* @JoinColumn(name="id", referencedColumnName="personId")
*/
private $address;
换句话说,如果Address
应该是Person
的所有者,那么您必须先将Address
保留在第一位,以便保持关联。
如果您希望将关联与Person
保持一致,则应将Person
标记为拥有方,将Address
标记为反向方:< / p>
/**
* @Id
* @OneToOne(targetEntity="Person", inversedBy="address")
* @JoinColumn(name="personId", referencedColumnName="id")
*/
private $person;
答案 1 :(得分:2)
您可以使用symfony listener并从地址实体中移除自动增量策略:
首先创建一个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 }
答案 2 :(得分:0)
我找到了一个解决方案,但我不知道这是否是最好的(对我来说看起来很重)。
这不是映射问题,而是我们坚持的方式:
而不是:
$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();
});
如果某人有更清洁的解决方案,他将获得赏金: - )。