walander
walander

Reputation: 55

Symfony2 - using entity value as default when form is submitted with empty fields

I'm building a web application in Symfony 2 where I have a User entity class for holding registered users.

Every user should have at least a username and a password of course, hence, I use the @Assert\NotBlank validation rule for both fields in the entity. The username is static, however, being able to change the password is desirable.

Thus, I'm building a form where signed in users can change their password (among other things). This form utilizes the repeated field type in Symfony for password confirmation purposes.

Now, in most cases I guess users are not looking to change their password, but rather update other fields in their profile. Therefore, I would want it to be possible for them to leave the password field blank. However, that will interfere with the NotBlank validation rule.

Removing the NotBlank validation rule is of course an option. Although, hardly the solution I'm looking for since that incures the problem of users ending up with a blank password.

Finally, As I understand it the symfony Request object autofills the form data back into the User entity on form submission.

Now, my question is: Is there any way to, upon empty password-field, make Symfony refill the User entity with the existing password from the database rather than a blank value? This would allow validation to follow through and won't cause any undesired changes to the password upon saving.

This is the code for the password field in the entity

/**
 * @var string
 * 
 * @ORM\Column(name="password",type="string",length=255) 
 * @Assert\NotBlank()
 * @Assert\Type(type="string")
 * @Assert\Length(max="255")
 * @Assert\NotEqualTo(value="password")
 */
private $password;

This is the code for generating the form

private function initForm($user) {
  return $this->createFormBuilder($user)
           ->add('name', 'text')
           ->add('mail', 'email')
           ->add('password', 'repeated', [
              'type' => 'password',
              'invalid_message' => 'The password fields must match.',
              'first_options' => ['label' => false],
              'second_options' => ['label' => false] //set to false to hide label in view
           ])
           ->add('save', 'submit')->getForm();
}

This is the Action-code for the /profile/edit page:

/**
 *  @ParamConverter("user",class="MyBundle:User")
 */
public function editAction(User $user, Request $request) {

   $form = $this->initForm($user);
   $form->handleRequest($request);

   if ($form->isValid()) {
      //update to database and redirect user back to profile_view
      $this->getDoctrine()->getManager()->flush();
      return $this->redirect($this->generateUrl('profile_view'));
   }

   return $this->render('MyBundle:User:edit.html.twig',
                        array('form' => $form->createView()));
}

Thanks in advance for any help!

EDIT

I did try the "obvious" approach, i.e. temporarilly storing the original password in a variable and "putting" it back if it was blank after the form was submitted:

/**
 * @ParamConverter("user",
                   class="MyBundle:User")
 */
public function editAction(User $user, Request $request) {
   $password = $user->getPassword();

   $form = $this->initForm($user)->getForm();
   $form->handleRequest($request);

   if ( $user->getPassword() == NULL ) {
       $user->setPassword($password);
       $form = $this->initForm($user)->getForm(); //this is the problem

   }

   if ( $form->isValid() ) {
       //update to database and redirect user back to profile view
       $this->getDoctrine()->getManager()->flush();
       return $this->redirect($this->generateUrl('profile_view'));
   }
   return $this->render('MyBundle:User:edit.html.twig',
                        array('form' => $form->createView()));
}

However, this did not work for apparent reasons. When the form is reinitialised within the if statement, Symfony finds the form has never been submitted in the first place, i.e. no validation is performed. And adding the $form->handleRequest($request) leaves us back with the first problem where the password field might be blank.

What I do find peculiar though. Symfony validates the Entity and not the form. But removing the problem row $form->initForm($user)->getForm() still didn't work, although the entity should be valid.

Upvotes: 0

Views: 1285

Answers (3)

greenseed
greenseed

Reputation: 529

I think the solution reside with a EVENT on the form it self.

  1. Will keep your validation intact when creating a new user

  2. Will put back the model data on the request data for a existing user with the password field empty

  3. Will trigger validation for a existing user with a password field empty in the model data(DB)

->addEventListener(FormEvents::PRE_SUBMIT,function(FormEvent $event) {

    /** @var array $requestData */
    $requestData = $event->getData();

    /** @var \AppBundle\Entity\User $user */
    $user = $event->getForm()->getData();

    if ($user && !$requestData['password']){
        $requestData['password'] = $user->getPassword();
    }

    $event->setData($requestData);
})

The FormEvents::PRE_SUBMIT will gave you access to 2 data type , who i commented with they respective type in the code.

$requestData is the normalized data from the user input this data is going to be apply on the model in this case $user , this will happen after the event PRE_SUBMIT.

Validation will happen on the $requestData but only after PRE_SUBMIT.

Upvotes: 1

Laurent W.
Laurent W.

Reputation: 3783

A solution is to use an extra class member $rawPassword, not stored in database, to be only used on your form. You will, of course, have keep your $password variable for database storage as defined in your question.

On your edit action, you just have to test after $form->bind($request); if your user as a $rawPassword password defined. If it's the case, update $password with the encoding value of $rawPassword, and flush.

EDIT : $form->bind($request); below 2.3, $form->handleRequest($request); otherwise.

Upvotes: 0

alexfv
alexfv

Reputation: 1876

I think this is not a good idea. The right way is create a separate form for changing user password. Examples: twitter, facebook, google

Upvotes: 1

Related Questions