Symfony2: How to change the current custom object using a form?

I am trying to add a simple form to allow my users to edit their profile. My problem:

Since the object "associated" with the form is the same as the current custom object ( $user === $entity

see below), if no form validation is performed, then the view is rendered with the modified custom object (that is, the values ​​of the invalid form).

Here's my (classic) controller:

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(),
    ));
}

      

So I figured out how to have two different objects $user

and $entity

. I used clone()

and worked well for the rendering part of the view (the object was $user

not modified), but it created a new entry in the database instead of updating the old one.

PS: I know what I should be using FOSUserBundle

. But I would really like to understand my mistake here :)

+3


source to share


2 answers


I used the same solution that FOSUserBundle calls $em->refresh()

in my entity when form validation fails:

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(),
    ));
}

      



Note that if you are using what is called a "virtual" field in " How to handle file uploads with Doctrine " (in my case "picture_file" you will need to clear it manually:

$em->refresh($user);
$user->picture_file = null; // here

      

+8


source


One approach is to always redirect:

    if ($form->isValid()) {
        $em->persist($entity);
        $em->flush();
    }
    return $this->redirect($this->generateUrl('profile'));

      

Of course you lose error messages and changes.

Another approach would be to define an object manager for your UserProvider only. $user

will no longer be the same as $entity

. A bit of extra overhead, but this certainly poses a problem and prevents similar interactions with other forms that might change all or part of the custom object.

Likewise, you reduce the overhead by creating an object manager just for your profile form. With this method, the overhead will only be incurred when editing the profile.



Finally, you might ask yourself if it really matters that the mapping data is not quite right in this particular case. Will it really bother you? Anyone besides you will notice?

See How to work with multiple entity managers in the Symfony cookbook

Another idea is to clone your custom object in your custom provider. This will strip it out of the entity manager.

You can also use $entityManager->detach($user);

to remove a user from the entity manager.

And why is the user token an object? Consider creating a completely independent User class with minimal information inferred from the database by your provider. This is what I am doing.

+1


source







All Articles