因此,我进行了一些挖掘和查找,并从 FOSUserBundle 中获得了一些线索。
要回答我的第一个问题,看起来两者都不是最好的选择,因此使第二个问题变得毫无意义。
我最终做了什么:
- 向我的用户实体添加一个新字段,
$plainPassword
. 然后我将我的 UserType 的映射更改为该字段,而不是直接更改 $password。
- 一定要
$plainPassword
填空User::eraseCredentials
- 制作了一个名为 UserManager 的新服务。在此,我给了它一个
updateUser()
处理密码实际编码的函数(不再由控制器处理,耶!)。
这或多或少是 FOSUserBundle 所做的。然后他们手动调用 updateUser(我相信来自控制器)。但是,我希望能够在很大程度上忘记这一步。这就是 Doctrine Events 发挥作用的地方。
我添加了两个 Doctrine 事件侦听器(都到 UserManager):prePersist 和 preUpdate。
两者都基本上检查我们是否正在处理用户对象,然后调用updateUser()
以更新用户的密码。preUpdate 必须有一个额外的步骤来手动设置新值。
为确保它触发,User::setPlainPassword()
清除密码并刷新我的盐(这是必要的,因为 plainPassword 不是映射属性,因此仅更改它不会允许用户触发 preUpdate)。
很多移动的部分,但现在我有了它,每当我User::setPlainPassword()
用来更改明文密码(无论在哪里),它现在都会正确编码并保存实际的密码值。哇!
namespace My\Bundle\UserBundle\DependencyInjection;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Event\LifecycleEventArgs;
use My\Bundle\UserBundle\Entity\User;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
class UserManager
{
protected $encoderFactory;
public function __construct(EncoderFactoryInterface $encoderFactory)
{
$this->encoderFactory = $encoderFactory;
}
public function getEncoder(User $user)
{
return $this->encoderFactory->getEncoder($user);
}
public function updateUser(User $user)
{
$plainPassword = $user->getPlainPassword();
if (!empty($plainPassword)) {
$encoder = $this->getEncoder($user);
$user->setPassword($encoder->encodePassword($plainPassword, $user->getSalt()));
$user->eraseCredentials();
}
}
public function preUpdate(PreUpdateEventArgs $event)
{
$user = $event->getEntity();
if (!($user instanceof \GamePlan\Bundle\UserBundle\Entity\User)) {
return;
}
$this->updateUser($user);
$event->setNewValue('password', $user->getPassword());
//die($event->getOldValue('password') . ' ' . $event->getNewValue('password') . ' ' . $event->hasChangedField('password') ? 'Y' : 'N');
}
public function prePersist(LifecycleEventArgs $event)
{
$user = $event->getEntity();
if (!($user instanceof \GamePlan\Bundle\UserBundle\Entity\User)) {
return;
}
$this->updateUser($user);
}
}
// In My\Bundle\UserBundle\Entity\User
public function setPlainPassword($plainPassword)
{
$this->plainPassword = $plainPassword;
// Change some mapped values so preUpdate will get called.
$this->refreshSalt(); // generates a new salt and sets it
$this->password = ''; // just blank it out
}
# In My/Bundle/UserBundle/Resources/config/services.yml
services:
my_user.manager:
class: My\Bundle\UserBundle\DependencyInjection\UserManager
arguments:
- @security.encoder_factory
tags:
- { name: doctrine.event_listener, event: prePersist }
- { name: doctrine.event_listener, event: preUpdate }