Doctrine自动更新未修改User实体问题排查求助
问题原因及解决方案
问题原因
- Doctrine的Unit of Work会自动跟踪所有托管实体(包括通过
$this->getUser()获取的当前登录用户)的属性变化。 - 你的User实体中,
eraseCredentials()方法执行了unset($this->roles),这个操作直接销毁了对象的$roles属性。Doctrine检测到该属性从初始数组状态变为不存在,会判定为属性被修改。 - 当调用
$manager->flush()时,Doctrine会尝试更新这个被“篡改”的User实体,但此时$roles属性已被销毁,对应数据库字段的值为null,触发了roles字段非空的约束错误。 - 注意:
eraseCredentials()通常会在认证完成后被Symfony Security自动调用,导致User实体的属性被意外修改。
解决方案
方法1:修正eraseCredentials()方法的逻辑
eraseCredentials()的设计初衷是清除临时敏感凭证(比如明文密码),而非销毁Doctrine映射的持久化属性。修改User实体的该方法:
public function eraseCredentials() { // 仅清除非持久化的敏感数据,比如临时存储的明文密码 // 不要操作Doctrine映射的$roles、$password等持久化字段 // 示例:如果有临时的plainPassword属性,只清除它 // unset($this->plainPassword); }
方法2:将User实体从Doctrine托管状态分离
如果需要保留原有eraseCredentials()逻辑,可以在获取用户后,将其从EntityManager的跟踪中移除,这样Doctrine就不会监控它的变化:
#[Route('/new', name: 'new', methods: ['POST'])] public function order( Request $request, ServiceRepository $serviceRepository, EntityManagerInterface $manager ): JsonResponse { $data = json_decode($request->getContent()); if (!property_exists($data, 'service')) return $this->json(['status' => false, 'message' => 'service is required']); $service = $serviceRepository->find($data->service); $user = $this->getUser(); $manager->detach($user); // 分离后Doctrine不再跟踪该实体 $order = new Order(); $order->setUser($user); $order->setCreatedAt(new \DateTimeImmutable()); $order->setTotal(0); $order->setStatus(Order::STATUS_OPEN); $order->setService($service); $manager->persist($order); $manager->flush(); return $this->json(['status' => true]); }
方法3:重新获取未被修改的User实例
通过UserRepository重新查询用户,获取一个干净的、未被eraseCredentials()修改过的托管实例:
#[Route('/new', name: 'new', methods: ['POST'])] public function order( Request $request, ServiceRepository $serviceRepository, UserRepository $userRepository, EntityManagerInterface $manager ): JsonResponse { $data = json_decode($request->getContent()); if (!property_exists($data, 'service')) return $this->json(['status' => false, 'message' => 'service is required']); $service = $serviceRepository->find($data->service); // 从仓库重新获取用户,避免使用被Security修改过的对象 $user = $userRepository->find($this->getUser()->getId()); $order = new Order(); $order->setUser($user); $order->setCreatedAt(new \DateTimeImmutable()); $order->setTotal(0); $order->setStatus(Order::STATUS_OPEN); $order->setService($service); $manager->persist($order); $manager->flush(); return $this->json(['status' => true]); }
内容的提问来源于stack exchange,提问作者mohamd Agoumi
相关产品推荐
相关产品推荐

