Search code examples
phpsymfony4

Symfony 4, edit user entity and save new password


I executed the make:crud command for generate some files for the entity user.

All works like a charm, but I have one problem when I edit a user. When I edit a user, I can :

  • change the password and save

or

  • don't modify the password and save

From the generated user 'edit' controller:

/**
 * @Route("/{id}/edit", name="user_edit", methods={"GET","POST"})
 */
public function edit(Request $request, User $user): Response
{
    $form = $this->createForm(UserType::class, $user);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {

        // HERE : How can I check if the password was changed ? 

        $this->getDoctrine()->getManager()->flush();

        return $this->redirectToRoute('user_index', [
            'id' => $user->getId(),
        ]);
    }

    return $this->render('user/edit.html.twig', [
        'user' => $user,
        'form' => $form->createView(),
        'title' => 'edit userr'
    ]);
}

How can I check if the password was changed ? The user variable contains the new password value...

If the password is new, I must use the encoder service. If not, I just must update the user with new data


Solution

  • For this it helps to have a class variable in your User that is not written to the database. In your form you can then use this variable to temporarily store the updated password in, that you then encode and remove. This is what the eraseCredentials()-method in the UserInterface is for.

    For example in your User you could have

    class User implements UserInterface
    {
        private $plainPassword;
    
        // ...
    
        public function setPlainPassword(string $plainPassword)
        {
            $this->plainPassword = $plainPassword;
        }
    
        public function getPlainPassword()
        {
            return $this->plainPassword;
        }
    
        public function eraseCredentials()
        {
            $this->plainPassword = null;
        }
    }
    

    Notice how private $plainPassword does not have any ORM-annotations, that means it will not be stored in the database. You can however use validation constraints, e.g. if you want to make sure that passwords have a minimum length or a certain complexity. You will still require the original password field that stores the encrypted password.

    You then add this field to your user update-form instead of the actual password field. In your controller you can then only check if the new plainPassword-field was filled out and then read the value, encode it and replace the actual password field.

    if ($form->isSubmitted() && $form->isValid()) {
        $user = $form->getData();
        if ($user->getPlainPassword() !== null) {
            $user->setPassword($this->userPasswordEncoder->encode(
                $user->getPlainPassword(),
                $user
            );
        }
    
        // ...
    

    Another way of doing this, without adding this "helper"-property to the user is using an unmapped form field:

    # UserForm
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            // ...
            ->add('plainPassword', PasswordType::class, ['mapped' => false])
        ;
    }
    

    The controller will look similar, only you fetch the data from the unmapped field instead of from the user:

     $form->get('plainPassword')->getData();