Symfony创建Conversation时的ORM关联错误及重复键问题
解决Symfony创建Conversation时的实体关联与重复插入问题
问题回顾
初始错误
A new entity was found through the relationship 'App\Entity\ReseauxSociaux\Conversation#utilisateur1' that was not configured to cascade persist operations for entity: John_Doe. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}).
添加cascade={"persist"}后新错误
"An exception occurred while executing a query: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'john_doe@gmail.com' for key 'UNIQ_8D93D649E7927C74'"
问题根源
- 游离态实体触发关联错误:代码中调用
$this->entityManager->getUnitOfWork()->clear(User::class);,将所有User实体从EntityManager管理缓存中移除。此时$this->getUser()拿到的登录用户实体变为游离态(不在ORM管理范围内),关联到新Conversation并执行persist时,ORM误判为新实体,抛出初始错误。 - 级联持久化误用:给
utilisateur1添加cascade={"persist"}后,ORM尝试持久化这个游离态User,但该用户已存在于数据库,邮箱字段的唯一约束导致重复插入报错。 - 对话存在检查逻辑失效:查询到已有对话后,直接用
new Conversation()覆盖变量,导致即使对话已存在仍会创建新实例,逻辑错误。
解决步骤
1. 移除不必要的clear(User::class)调用
这是问题的核心诱因,无特殊业务需求时不要随意清除ORM实体缓存:
// 删除此行代码 $this->entityManager->getUnitOfWork()->clear(User::class);
2. 修复对话存在检查逻辑
双向检查用户组合(避免user1/user2顺序导致漏判),若已有对话直接返回:
// Check if conversation already exists $conversation = $this->conversationRepository->findConversationByUser1User2( $otherUser->getId(), $userConnected->getId() ); // 反向检查用户顺序的情况 if (!$conversation) { $conversation = $this->conversationRepository->findConversationByUser1User2( $userConnected->getId(), $otherUser->getId() ); } // 已有对话则直接返回 if ($conversation) { return $this->json($conversation, 200, [], ['groups' => 'message:read']); } // 无对话时再创建新实例 $conversation = new Conversation();
3. 移除关联的cascade={"persist"}配置
User实体已存在于数据库,无需让Conversation触发其持久化,恢复实体关联配置:
/** * @ORM\ManyToOne(targetEntity=User::class, inversedBy="conversations") */ private $utilisateur1; /** * @ORM\ManyToOne(targetEntity=User::class) */ private $utilisateur2;
(可选)若必须保留clear(User::class)
如果业务逻辑要求清除User缓存,需将游离态的登录用户合并回EntityManager:
$userConnected = $this->getUser(); // 合并游离态实体到ORM管理 $userConnected = $this->entityManager->merge($userConnected);
完整修复后的控制器代码
/** * @Route("/new-conversation-user", name="newConversationUser", methods={"POST"}) */ public function newConversationUser(Request $request) { $donnees = json_decode($request->getContent()); $otherUser = $this->entityManager->getRepository(User::class)->find($donnees->id); if (is_null($otherUser)) { throw new \Exception("L'utilisateur n'a pas été trouvé"); } $userConnected = $this->getUser(); if ($otherUser->getId() === $userConnected->getId()) { throw new \Exception("C'est profond mais vous ne pouvez pas créer une conversation avec vous-même"); } // 双向检查对话是否存在 $conversation = $this->conversationRepository->findConversationByUser1User2( $otherUser->getId(), $userConnected->getId() ); if (!$conversation) { $conversation = $this->conversationRepository->findConversationByUser1User2( $userConnected->getId(), $otherUser->getId() ); } if ($conversation) { return $this->json($conversation, 200, [], ['groups' => 'message:read']); } $conversation = new Conversation(); $conversation->setUtilisateur1($userConnected); $conversation->setUtilisateur2($otherUser); $this->entityManager->persist($conversation); $this->entityManager->flush(); return $this->json($conversation, 201, [], ['groups' => 'message:read']); }
内容的提问来源于stack exchange,提问作者nyaina
相关产品推荐
相关产品推荐

