Doctrine同一属性能否关联两个目标属性?Symfony OneToOne映射问题咨询
问题解答
潜在异常行为
- 反向关联完全失效:Doctrine只会读取Time类$user属性上的第一个
@OneToOne注解,第二个会被直接丢弃。当Time实例关联的是User的time2字段时,你从Time侧调用$time->getUser()只会拿到空值,无法正确获取关联用户。 - 级联操作异常:你配置的
cascade={"persist"}只会对关联到User#time字段的Time实例生效,time2对应的Time实例持久化时不会触发级联,极端情况会出现外键约束报错。 - 数据一致性问题:Doctrine的UnitOfWork在计算实体变更时,会因为映射不匹配丢失
time2的关联数据,偶发赋值后持久化到数据库time2_id字段为空的问题,你当前使用的doctrine/common v2.11版本存在较多这类映射不兼容的历史问题。 - 维护性障碍:执行
php bin/console doctrine:schema:validate命令会直接报错,后续生成迁移文件时可能生成错误的SQL,比如遗漏time2_id的外键约束,或者生成多余的冗余字段。
正确实现方案
你不需要拆分Time实体,只需要把Time类中的反向关联拆分为两个独立属性即可,原有eventListener逻辑可以完全复用:
修正后的Time实体代码
class Time { // ... 原有属性和方法 /** * @ORM\OneToOne(targetEntity=User::class, mappedBy="time", cascade={"persist"}) */ private $user; /** * @ORM\OneToOne(targetEntity=User::class, mappedBy="time2", cascade={"persist"}) */ private $user2; // 可新增辅助方法统一获取关联用户,不影响原有业务逻辑 public function getRelatedUser(): ?User { return $this->user ?? $this->user2; } // ... }
修正后的User实体代码
仅需要修改inversedBy属性对应到Time类的两个反向属性即可:
class User { // ... 原有属性和方法 /** * @ORM\OneToOne(targetEntity=Time::class, inversedBy="user", cascade={"persist"}) * @ORM\JoinColumn(name="time_id", referencedColumnName="id", nullable=true, onDelete="SET NULL") */ private $time; /** * @ORM\OneToOne(targetEntity=Time::class, inversedBy="user2", cascade={"persist"}) * @ORM\JoinColumn(name="time2_id", referencedColumnName="id", nullable=true, onDelete="SET NULL") */ private $time2; // 可新增赋值时自动设置type的逻辑,避免类型混淆 public function setTime(?Time $time): self { if ($time instanceof Time) { $time->setType(0); } $this->time = $time; return $this; } public function setTime2(?Time $time2): self { if ($time2 instanceof Time) { $time2->setType(1); } $this->time2 = $time2; return $this; } // ... }
修改后执行php bin/console doctrine:schema:validate即可消除映射错误,原有业务逻辑不需要做大幅调整。
内容的提问来源于stack exchange,提问作者circulosmeos
相关产品推荐
相关产品推荐

