【问题标题】:Doctrine2 Composite key with foreign key - Embedded form issue带有外键的 Doctrine2 复合键 - 嵌入式表单问题
【发布时间】:2014-10-14 08:19:26
【问题描述】:

让我们考虑以下场景。

我有这些实体

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 实体创建了一个表单,我可以(或不能)为 i18n 插入一些文本信息。
当我将请求绑定到我的表单并保留 Room 对象时,我收到了该错误(* 是故意放在那里的)

app.ERROR: ***** 中的异常:类型为 ******\Entity\RoomI18N 的实体 缺少字段“房间”的分配 ID。标识符生成 此实体的策略需要先填充 ID 字段 EntityManager#persist() 被调用。如果要自动生成 标识符,而不是您需要调整元数据映射 因此。 [] []

我很清楚发生了什么事:教义尝试在房间对象 BEFORE 之前保留 RoomI18N 对象,因此,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
}

【问题讨论】:

  • Room::addRoomI18n() 是否也将 Room 设置在传递的 RoomI18N 上?类似于public function addRoomI18N(RoomI18N $roomI18N) { $this->room_i18n->add($roomI18N); $roomI18N->setRoom($this); }
  • 啊,我看你已经想通了 :)
  • @JasperN.Brouwer:你的方向是对的,请看下面我的回答
  • 我仍然建议您使用 Room::addRoomI18n() 方法来解决这个问题。这样你就不会在其他地方忘记它(比如控制器)。
  • @JasperN.Brouwer:是的,当然。我只是根据我的问题提出“最快”的解决方案:) 我已经在 addRoom18n 函数中完成了

标签: symfony doctrine-orm


【解决方案1】:

您可以在主实体中添加外键实体的同时添加主键实体实例,如下所示..

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

【讨论】:

  • 是的,这正是我所做的,如果您阅读 cmets(3 小时前!!),您会注意到我的答案中的 sn-p 代码是这样写的,只是因为问题是这样写,我不会修改整个事情,但贾斯珀对你也这么说,我同意。这个问题,对我来说,不需要考虑,应该删除
  • 别担心 :) 顺便说一句,我已经更新了自己的答案。我想现在大家都清楚了:)
【解决方案2】:

好的,我找到了解决方案。
我需要明确地将反面对象 (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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-15
    相关资源
    最近更新 更多