我正在尝试添加一个简单的表单,以允许我的用户编辑他们的个人资料。我的问题是:
由于与表单“链接”的实体与当前用户对象($user === $entity
相同,请参见下文),如果表单验证失败,则使用修改后的用户对象呈现视图(即。使用无效表格的值。)
这是我的(经典)控制器:
public function profileAction()
{
$em = $this->getDoctrine()->getEntityManager();
$user = $this->get('security.context')->getToken()->getUser();
$entity = $em->getRepository('AcmeSecurityBundle:User')->find($user->id);
// $user === $entity => true
$form = $this->createForm(new ProfileType(), $entity);
$request = $this->getRequest();
if ($request->getMethod() === 'POST')
{
$form->bindRequest($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('profile'));
}
}
return $this->render('AcmeSecurityBundle:User:profile.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
所以我想知道如何有两个区别对象$user
和$entity
。我使用clone()
,它适用于视图渲染部分($user
对象未被修改),但它在数据库中创建了一条新记录,而不是更新旧记录。
PS:我知道我应该使用FOSUserBundle
。但我真的想在这里理解我的错误:)。
答案 0 :(得分:9)
我使用与FOSUserBundle相同的解决方案,当表单验证失败时,我在实体上调用$em->refresh()
:
public function profileAction()
{
$em = $this->getDoctrine()->getEntityManager();
$user = $this->get('security.context')->getToken()->getUser();
$entity = $em->getRepository('AcmeSecurityBundle:User')->find($user->id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$form = $this->createForm(new ProfileType(), $entity);
$request = $this->getRequest();
if ($request->getMethod() === 'POST')
{
$form->bindRequest($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('profile'));
}
$em->refresh($user); // Add this line
}
return $this->render('AcmeSecurityBundle:User:profile.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
请注意,如果您在“How to handle File Uploads with Doctrine”中使用所谓的“虚拟”字段(在我的情况下为“picture_file”,您需要手动清除它:
$em->refresh($user);
$user->picture_file = null; // here
答案 1 :(得分:1)
一种方法是始终重定向:
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
}
return $this->redirect($this->generateUrl('profile'));
当然,您会丢失错误消息和更改。
另一种方法是为您的UserProvider定义一个实体管理器。 $user
将不再与$entity
相同。一些额外的开销,但它肯定会使问题发生,并将阻止与可能修改用户实体的全部或部分的其他表单的类似交互。
以类似的方式,您可以通过仅为您的个人资料表单创建实体管理器来减少开销。使用此方法时,只会在编辑配置文件时产生开销。
最后,你可以问自己,在这种特殊情况下,显示数据是否真的很重要。真的会打扰什么吗?有人会注意到你吗?
请参阅How to work with Multiple Entity Managers in Symfony Cookbook
另一个想法是在您的用户提供程序中克隆您的用户实体。这会将它与实体经理分开。
您还可以使用$entityManager->detach($user);
从实体管理器中删除用户。
为什么令牌用户仍然是实体?考虑创建一个完全独立的User类,用户提供程序从数据库中提取最少的信息。这就是我的工作。