Symfony2:如何使用表单修改当前用户的实体?

时间:2012-03-21 20:42:47

标签: php symfony doctrine-orm

我正在尝试添加一个简单的表单,以允许我的用户编辑他们的个人资料。我的问题是:

由于与表单“链接”的实体与当前用户对象($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。但我真的想在这里理解我的错误:)。

2 个答案:

答案 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类,用户提供程序从数据库中提取最少的信息。这就是我的工作。