Doctrine - preUpdate事件订阅者根本没有被解雇

时间:2013-11-20 15:00:19

标签: php events symfony doctrine-orm sql-update

我实现了一个简单的密码更改算法。这是代码:

public function postChangePasswordAction(Request $request, $username)
    {
        $em = $this->getDoctrine()->getManager();
        $user = $em->getRepository("TskUserBundle:User")->findOneByUsername($username);
        if(!$user)
        {
            throw $this->createNotFoundException("User not found!");
        }
        $form = $this->createPasswordChangeForm();
        $form->handleRequest($request);
        $old_password = $form->get('old_password')->getData();
        $new_password = $form->get("new_password")->getData();
        if($form->isValid())
        {
            $old_password_encoded = $this->get("tsk_encoder")->encode($user, $old_password, $user->getSalt());
            if($old_password_encoded == $user->getPassword())
            {
                $user->setPlainPassword($new_password);
                $user->setFirstname("rofa");
                $em->flush();
                return $this->redirect($this->generateUrl("tsk_user_profile", array("username"=>$user->getUsername())));
            } else { return new Response($old_password_encoded."\n".$user->getPassword());}
        }

        return $this->render("TskUserBundle:User:change_password.html.twig", array("username" => $user->getUsername(), "form"=>$form->createView()));
    }

我的User实体是:

<?php

namespace Tsk\UserBundle\Entity;

use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity(repositoryClass="Tsk\UserBundle\Repository\UserRepository")
 * @ORM\Table(name="users")
 * @ORM\HasLifecycleCallbacks
 * @Assert\GroupSequence({"FormCreate", "FormEdit", "User"})
 */
class User implements AdvancedUserInterface, \Serializable
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=25, unique=true)
     * @Assert\NotBlank(groups={"FormEdit", "FormCreate"})
     */
    protected $username;

    /**
     * @Assert\NotBlank(groups={"FormCreate"})
     * @Assert\Length(min="4", minMessage="Password must be 4 characters at least", groups={"FormCreate"})
     */
    protected $plain_password;

    /**
     * @ORM\Column(type="string", length=128)
     * @Assert\NotBlank
     */
    protected $password;

    /**
     * @ORM\Column(type="string", length=50, unique=true)
     * @Assert\Email(groups={"FormEdit", "FormCreate"});
     */
    protected $email;

    /**
     * @ORM\Column(type="boolean")
     */
    protected $is_active;

    /**
     * @ORM\Column(type="string", length=59)
     * @Assert\NotBlank(groups={"FormEdit", "FormCreate"})
     */
    protected $firstname;

    /**
     * @ORM\Column(type="string", length=59)
     * @Assert\NotBlank(groups={"FormEdit", "FormCreate"})
     */
    protected $lastname;

    /**
     * @ORM\ManyToMany(targetEntity="Role", inversedBy="users", cascade={"persist"})
     * @Assert\NotBlank
     */
    protected $roles;

    /**
     * @ORM\Column(type="string", length=50)
     */
    protected $salt;

    public function __construct()
    {
        $this->roles = new ArrayCollection();
        $this->salt = md5(uniqid(null, true));
    }

    /**
     * @ORM\PrePersist
     */
    public function setIsActiveValue()
    {
        $this->setIsActive(true);
    }

    public function getPlainPassword()
    {
        return $this->plain_password;
    }

    public function setPlainPassword($plain_password)
    {
        $this->plain_password = $plain_password;
        return $this;
    }

    public function setPassword($password)
    {
        $this->password = $password;
        return $this;
    }

    public function isAccountNonExpired()
    {
        return true;
    }

    public function isAccountNonLocked()
    {
        return true;
    }

    public function isCredentialsNonExpired()
    {
        return true;
    }

    public function isEnabled()
    {
        return $this->getIsActive();
    }

    public function getRoles()
    {
        return $this->roles->toArray();
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function getSalt()
    {
        return $this->salt;
    }

    public function getUsername()
    {
        return $this->username;
    }

    /**
     * Removes sensitive data from the user.
     *
     * This is important if, at any given point, sensitive information like
     * the plain-text password is stored on this object.
     */
    public function eraseCredentials()
    {

    }

    public function serialize()
    {
        return serialize(array($this->getUsername()));
    }


    public function unserialize($serialized)
    {
        list($this->username) = unserialize($serialized);
    }


    public function getId()
    {
        return $this->id;
    }

    public function setUsername($username)
    {
        $this->username = $username;

        return $this;
    }

    public function setEmail($email)
    {
        $this->email = $email;

        return $this;
    }

    public function getEmail()
    {
        return $this->email;
    }

    public function setIsActive($isActive)
    {
        $this->is_active = $isActive;

        return $this;
    }

    public function getIsActive()
    {
        return $this->is_active;
    }

    public function setFirstname($firstname)
    {
        $this->firstname = $firstname;

        return $this;
    }

    public function getFirstname()
    {
        return $this->firstname;
    }

    public function setLastname($lastname)
    {
        $this->lastname = $lastname;

        return $this;
    }

    public function getLastname()
    {
        return $this->lastname;
    }

    public function addRole(Role $roles)
    {
        $this->roles[] = $roles;

        return $this;
    }

    public function removeRole(Role $roles)
    {
        $this->roles->removeElement($roles);
    }

}

我有prePersistpreUpdate的活动订阅者。当我创建一个新用户时,它正常被解雇。但是,当我只更改密码时,它根本不起作用。 (请注意plain_password属性是虚拟属性。不会保存在数据库中)

以下是代码:

<?php
namespace Tsk\UserBundle\EventSubscriber;

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Tsk\UserBundle\Entity\User;
use Tsk\UserBundle\Utils\Encoder;

class EncoderSubscriber implements EventSubscriber
{
    protected $encoder;

    public function __construct(Encoder $ef)
    {
        $this->encoder = $ef;
    }

    /**
     * Returns an array of events this subscriber wants to listen to.
     */
    public function getSubscribedEvents()
    {
        return array(
            'preUpdate',
            'prePersist',
        );
    }

    public function preUpdate(LifecycleEventArgs $args)
    {
        $this->encode($args);
    }

    public function prePersist(LifecycleEventArgs $args)
    {
        $this->encode($args);
    }

    protected function encode(LifecycleEventArgs $args)
    {
        $user = $args->getEntity();

        if($user instanceof User)
        {
            $encodedPassword = ($this->encoder->encode($user, $user->getPlainPassword(), $user->getSalt()));
            $user->setPassword($encodedPassword);
        }
    }
}
?>

1 个答案:

答案 0 :(得分:4)

我也遇到了同样的问题。

原因是Doctrine的工作单元无法计算原始实体和密码更改实体之间的更改集,因为plain_password不会持久保存到数据库中,因此工作单元忽略plain_password字段的更改(或者我应该说,工作单元在调用flush之前和调用flush之后发现数据之间没有差异。)

让它发挥作用的方法是在致电password之前更新EntityManager#flush。我通过在检测到密码更改时调度UserEvents::CHANGE_PASSWORD来实现此功能(当然是手动)。使用此方法,如果您只是要更新其中的密码字段,则无需再订阅preUpdate事件。

您还可以参考FOSUserBundle的实施。