Symfony3+Doctrine2.6删除实体的提交顺序与完整性约束问题
你碰到的这个问题其实是Doctrine和MySQL外键约束共同作用的典型场景——之前你以为remove和persist的调用顺序不影响执行,其实在涉及外键关联的删除操作里,顺序直接决定了数据库会不会抛出完整性约束错误。
为什么调整顺序就正常了?
先拆解你的场景里的依赖关系:
你的Offer实体关联了同一个MenuComponent,也就是说**Offer的表中存在指向MenuComponent的外键**。MySQL的外键约束要求:被引用的记录(这里是MenuComponent)不能在引用它的记录(Offer)之前被删除——不然数据库就会报错说「还有其他数据指着它呢,不能删」。
之前你先调用$em->remove($menuComponent),此时Offer还在数据库里且关联着它,自然触发了Integrity constraint violation;把remove($offer)移到前面后,先删了引用MenuComponent的Offer,再删MenuComponent就符合约束要求了。
判断删除顺序的简单规则
一句话总结:先删「引用别人的实体」,再删「被别人引用的实体」,或者更直白点:
- 先处理那些「依赖于其他实体才能存在」的对象(持有外键的一方)
- 最后处理被依赖的对象(被外键指向的一方)
具体到实体关联的判断方法:
- 看Doctrine映射的关联类型:如果是
ManyToOne,那么「多」的那一方持有外键,是引用者;「一」的那一方是被引用者。比如Offer关联Component是ManyToOne,那Offer是引用者,Component是被引用者,要先删Offer再删Component。 - 对于双向关联,找到反向关联的
mappedBy,mappedBy所在的实体是被引用者,另一方是引用者。
通用API引擎的抽象方案
如果要做类似Api Platform的通用删除逻辑,推荐两种思路:
利用Doctrine级联删除:在实体关联的映射里配置
cascade={"remove"},让Doctrine自动帮你处理删除顺序。比如在Menu的offers关联上加上这个配置:/** * @OneToMany(targetEntity="Offer", mappedBy="menu", cascade={"remove"}) */ private $offers;这样调用
$em->remove($menu)时,Doctrine会自动先删除所有关联的Offer,再删Menu,完全不用手动管顺序。但要注意:级联删除一定要谨慎,避免误删关联数据。自动分析依赖顺序:如果不能用级联删除,就需要在删除前递归遍历实体的所有反向关联(即其他实体中指向当前实体的外键关联),先删除这些反向关联的实体,再删除当前实体。这个逻辑可以抽象成通用的删除服务,处理所有实体的删除流程。
内容的提问来源于stack exchange,提问作者Stnaire

