1
votes

I've an entity with a plainPassword and a password attribute. In form, I map on the plainPassword. After, when the user valid the form, I do password validation on the plainPassword.

To encode the password, I use an EventSubscriber that listen on prePersist and preUpdate. It works well for the register form, because it's a new entity, the user fill some persisted attributes, then doctrine persist it and flush.

But, when I just want to edit the password, it doesn't work, I think it's because the user just edit a non persisted attribute. Then Doctrine doesn't try to persist it. But I need it, to enter in the Subscriber.

Someone know how to do it ? (I've a similar problem in an other entity) For the moment, I do the operation in the controller...

Thanks a lot.

My UserSubscriber

  class UserSubscriber implements EventSubscriber
  {
      private $passwordEncoder;
      private $tokenGenerator;

      public function __construct(UserPasswordEncoder $passwordEncoder, TokenGenerator $tokenGenerator)
      {
          $this->passwordEncoder = $passwordEncoder;
          $this->tokenGenerator = $tokenGenerator;
      }

      public function getSubscribedEvents()
      {
          return array(
              'prePersist',
              'preUpdate',
          );
      }

      public function prePersist(LifecycleEventArgs $args)
      {
          $object = $args->getObject();
          if ($object instanceof User) {
              $this->createActivationToken($object);
              $this->encodePassword($object);
          }
      }

      public function preUpdate(LifecycleEventArgs $args)
      {
          $object = $args->getObject();
          if ($object instanceof User) {
              $this->encodePassword($object);
          }
      }

      private function createActivationToken(User $user)
      {
          // If it's not a new object, return
          if (null !== $user->getId()) {
              return;
          }

          $token = $this->tokenGenerator->generateToken();
          $user->setConfirmationToken($token);
      }

      private function encodePassword(User $user)
      {
          if (null === $user->getPlainPassword()) {
              return;
          }

          $encodedPassword = $this->passwordEncoder->encodePassword($user, $user->getPlainPassword());
          $user->setPassword($encodedPassword);
      }

My user Entity:

class User implements AdvancedUserInterface, \Serializable
{
/**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

/**
 * @ORM\Column(name="email", type="string", length=255, unique=true)
 * @Assert\NotBlank()
 * @Assert\Email()
 */
private $email;

/**
 * @Assert\Length(max=4096)
 */
private $plainPassword;

/**
 * @ORM\Column(name="password", type="string", length=64)
 */

private $password;

ProfileController:

class ProfileController extends Controller 
{
/**
 * @Route("/my-profile/password/edit", name="user_password_edit")
 * @Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")
 */
public function editPasswordAction(Request $request)
{
    $user = $this->getUser();
    $form = $this->createForm(ChangePasswordType::class, $user);
    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {
        // Encode the password
        // If I decomment it, it's work, but I want to do it autmaticlally, but in the form I just change the plainPassword, that is not persisted in database
        //$password = $this->get('security.password_encoder')->encodePassword($user, $user->getPlainPassword());
        //$user->setPassword($password);
        $em = $this->getDoctrine()->getManager();
        $em->flush();
        $this->addFlash('success', 'Your password have been successfully changed.');
        return $this->redirectToRoute('user_profile');
    }
    return $this->render('user/password/edit.html.twig', [
        'form' => $form->createView(),
    ]);
}

}

1
can you show entity?Imanali Mamadiev
Yes, the entity exist, I can see it, when I edit the password, I can see the plainPassword set if I do a dump, but plainPassword is not persisted, then I think doctrine do nothing... Maybe there is a possibilities to force Doctrine persist ?mpiot
This is the normal behavior because you should never save a password in plain text.gp_sflover
Could you add your User class and the relevant code from the Controller to your question?Pete
@gp_sflover: Yes I know I encode the password with bcrypt, but I want the encryption be done automatically, not always call the security.password_encoder in the controller.mpiot

1 Answers

1
votes

You can force Doctrine to mark an object as dirty by manipulating the UnitOfWork directly.

$em->getUnitOfWork()->scheduleForUpdate($entity)

However, I do strongly discourage this approach as it violates carefully crafted abstraction layers.