Doctrine2具有外键的复合键 - 嵌入式表单问题

时间:2014-10-14 08:19:26

标签: symfony doctrine-orm

让我们考虑以下情况。

我有这些实体

class RoomI18N
{
    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="TextType")
     * @ORM\JoinColumn(name="text_type_id", referencedColumnName="id")
     */
    protected $text_type;

    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Language")
     * @ORM\JoinColumn(name="language_id", referencedColumnName="id")
     */
    protected $language;

    /**
     * @ORM\id
     * @ORM\ManyToOne(targetEntity="Room", inversedBy="room_i18n")
     */
    protected $room;

    [...]
}

class Room
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    [...]

    /**
     * @ORM\OneToMany(targetEntity="RoomI18N", mappedBy="room", cascade={"remove", "persist"})
     */
    protected $room_i18n;

    [...]
}

如您所见,RoomI18N实体具有由其他表(实体)的三个主键组成的复合键。这是设计数据库时非常常见的方法,这也会节省“空间”,因为我不需要存储我不需要的额外字段(id)。此外,数据复制是最小的,RoomI18N(复合)密钥不会存储在表的其他位置。

说,我遇到了麻烦,因为RoomI18N是关系的拥有方,我为Room实体创建了一个表单,我可以 - 或者不 - 插入一些文本信息国际化。
当我将请求绑定到我的表单并持久保存Room对象时,我收到该错误(*故意放在那里)

  

app.ERROR:*****中的例外:****** \ Entity \ RoomI18N类型的实体   缺少字段'room'的已分配ID。标识符生成   此实体的策略要求之前填充ID字段   调用EntityManager#persist()。如果要自动生成   您需要调整元数据映射   因此。 [] []

我很清楚发生了什么:doctrine尝试持久化RoomI18N对象 BEFORE 房间对象,因此,Room对象仍然没有ID。

好的,如果出现以下情况,我该如何摆脱这种情况:

  • 我希望避免将ID字段设为RoomI18N主键
  • 我宁愿不做一些“奇怪”的黑客攻击:从Room对象中删除RoomI18N相关对象,保留它,将它们再次放入原始Room对象并再次保存到DB

此外,当然,我已经尝试删除cascade挂钩并手动持久化对象 - 只是为了尝试,我确信这不起作用 - 当然没有任何改变。


更新

我控制器我将所有RoomI18N设置如下

$handled_languages = $lodging->getHandledLanguages();
[...]
foreach ($handled_languages as $language) {
    $room_text_i18n = new RoomI18N();
    $room_text_i18n->setLanguage($language);
    [...]
    $room->addRoomI18n($room_text_i18n); //fetched before
}

2 个答案:

答案 0 :(得分:0)

好的,我找到了解决办法 我需要显式将反转的对象(Room)设置为拥有的一方(RoomI18N)。

基本上我改变了控制器的代码如下

$handled_languages = $lodging->getHandledLanguages();
[...]
foreach ($handled_languages as $language) {
    $room_text_i18n = new RoomI18N();
    $room_text_i18n->setLanguage($language);
    [...]
    $room->addRoomI18n($room_text_i18n); //fetched before
    $room_text_i18n->setRoom($room);
}

提示来自:enter link description here


更新

正如Jasper所说,最好修改addRoomI18n()函数(Room实体,所以反面),如下所示

public function addRoomI18n(RoomI18N $roomI18n)
{
    $this->room_i18n[] = $roomI18n;
    $roomI18n->setRoom($this); //As is the owning side, I need to set it explicitly otherwise I need to set everytime 
                               //I want to persist a Room object

    return $this;
}

答案 1 :(得分:0)

您可以在主实体中添加外键实体时添加主键实体实例的实例。

 public function addRoomI18n(RoomI18n $RoomI18n)
        {
            $this->RoomI18n[] = $RoomI18n;
            $RoomI18n->setRoom($this);
            return $this;
        }