如何仅在满足条件时更新映射字段?
How can I update mapped fields only when a condition is satisfied?
在我的 Symfony2 应用程序中,我创建了一个自定义表单来编辑 User
class 的对象。用户有一个 password
属性,其中包含用户密码的哈希值。出于显而易见的原因,我不想将此 属性 的值回显到字段中。但是,我希望我的表单上有一个密码字段,以便在编辑用户时可以更改用户的密码。
此密码字段应如下所示:
如果用户设置了密码,则该字段应包含********
.
如果用户没有设置密码,则该字段应为空。
- (事实证明前两点在我目前的架构下是不可能实现的,所以我打算:)当页面加载时,该字段应该是空的,不管用户是否有密码设置。
- 如果该字段是带内容发布的,则用户密码应设置为该字段的哈希值。
- 如果该字段为空,则不应更改用户密码,更重要的是,不要清除。
我想用自定义数据转换器来实现它。但是,当密码字段为空时,数据转换器没有为我提供跳过更新用户 password
属性 的方法。
我需要在何处扩展框架以添加决定应更新哪些字段的自定义逻辑?
更新
这是我要替换的遗留代码:
/* SomeController.php */
$pass = $request->get('password');
if (strlen($pass) >= 5 && strlen($pass) <= 16) {
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($pass, $user->getSalt());
$user->setPassword($password);
}
我可以忍受删除字符串长度检查。我只想检查是否输入了某些内容。
如您所见,我不能简单地将此代码移动到数据转换器,因为它需要访问我们当前正在编辑的用户 $user
。我认为创建提供这种价值的服务不是一个好主意。
只需将控件直接插入您的实体方法并使用数据转换器(如您所见)
因此您的实体将是
class User
{
//some properties and methods here
public function setPassword($pwd = null) {
if (null !== $pwd) {
$this->password = //do actions here like hash or whatever
}
//don't update the password
}
}
如果您只想利用 DataTransformer,您仍然可以按需要做事
use Symfony\Component\DependencyInjection\ContainerInterface;
class PasswordTransformer implements DataTransformerInterface
{
private $ci;
public function __construct(ContainerInterface $ci) {
$this->ci = $ci;
}
//...
public function reverseTransform($form_password) {
if (!$form_password) {
//return password already stored
return $this->ci->get('security.context')
->getToken()
->getUser()
->getPassword();
}
}
}
当然你需要将 service_container
服务注入你的数据转换器(或者更好的是,你应该将它注入你的表单类型的选择器并传递给 DataTransformer 构造函数,如下所示:
services:
your.bundle.password_selector_type:
class: Your\Bundle\Form\Type\PasswordSelectorType
arguments: ["@security.context"]
tags:
- { name: form.type, alias: password_selector_type }
对于表单部分,您应该看看这个小部件。
http://symfony.com/doc/current/reference/forms/types/repeated.html
它提供了一种询问和处理字段确认的简单方法(当您将类型设置为密码时,它还会用星号隐藏值)。
$builder->add('password', 'repeated', array(
'type' => 'password',
'invalid_message' => 'The password fields must match.',
'options' => array('attr' => array('class' => 'password-field')),
'required' => true,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password')));
它将检查第一个和第二个选项是否相等。如果是这种情况,那么您的表格将被视为有效。否则,将显示 invalid_message,第一个字段将设置为用户键入的内容,同时确认字段(第二个选项)将被清空。
您可以在之后添加一些逻辑,例如对密码进行哈希处理以最终保留您的实体。 (在表单处理程序中提取它是一个很好的做法)。
这是我目前想到的,但我对该解决方案不满意,因为它涉及控制器中的自定义表单处理。然而,到目前为止,这是我找到的唯一方法。
我的表单 class 为用户密码添加了一个未映射的字段:
class UserType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('username')
->add('displayName')
->add('password', 'password', ['mapped' => false]);
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array('data_class' => 'MyCompany\MyBundle\Entity\User'));
}
public function getName() {
return 'user';
}
}
然后在我的控制器中手动处理此字段class:
class UserAdminController extends Controller {
public function editUserAction($userId, Request $request) {
$user = $this->getDoctrine()->getRepository('MyCompanyMyBundle:User')->findOneById($userId);
$form = $this->createForm('user', $user);
$form->handleRequest($request);
if ($form->isValid()) {
$newPassword = $form['password']->getData();
if ($newPassword !== "") {
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($newPassword, $user->getSalt());
$user->setPassword($password);
}
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
}
return $this->render(
"MyCompanyMyBundle:Admin/Management/User:Edit.html.php",
[
"form" => $form->createView()
]
);
}
}
在我的 Symfony2 应用程序中,我创建了一个自定义表单来编辑 User
class 的对象。用户有一个 password
属性,其中包含用户密码的哈希值。出于显而易见的原因,我不想将此 属性 的值回显到字段中。但是,我希望我的表单上有一个密码字段,以便在编辑用户时可以更改用户的密码。
此密码字段应如下所示:
如果用户设置了密码,则该字段应包含********
.如果用户没有设置密码,则该字段应为空。- (事实证明前两点在我目前的架构下是不可能实现的,所以我打算:)当页面加载时,该字段应该是空的,不管用户是否有密码设置。
- 如果该字段是带内容发布的,则用户密码应设置为该字段的哈希值。
- 如果该字段为空,则不应更改用户密码,更重要的是,不要清除。
我想用自定义数据转换器来实现它。但是,当密码字段为空时,数据转换器没有为我提供跳过更新用户 password
属性 的方法。
我需要在何处扩展框架以添加决定应更新哪些字段的自定义逻辑?
更新
这是我要替换的遗留代码:
/* SomeController.php */
$pass = $request->get('password');
if (strlen($pass) >= 5 && strlen($pass) <= 16) {
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($pass, $user->getSalt());
$user->setPassword($password);
}
我可以忍受删除字符串长度检查。我只想检查是否输入了某些内容。
如您所见,我不能简单地将此代码移动到数据转换器,因为它需要访问我们当前正在编辑的用户 $user
。我认为创建提供这种价值的服务不是一个好主意。
只需将控件直接插入您的实体方法并使用数据转换器(如您所见)
因此您的实体将是
class User
{
//some properties and methods here
public function setPassword($pwd = null) {
if (null !== $pwd) {
$this->password = //do actions here like hash or whatever
}
//don't update the password
}
}
如果您只想利用 DataTransformer,您仍然可以按需要做事
use Symfony\Component\DependencyInjection\ContainerInterface;
class PasswordTransformer implements DataTransformerInterface
{
private $ci;
public function __construct(ContainerInterface $ci) {
$this->ci = $ci;
}
//...
public function reverseTransform($form_password) {
if (!$form_password) {
//return password already stored
return $this->ci->get('security.context')
->getToken()
->getUser()
->getPassword();
}
}
}
当然你需要将 service_container
服务注入你的数据转换器(或者更好的是,你应该将它注入你的表单类型的选择器并传递给 DataTransformer 构造函数,如下所示:
services:
your.bundle.password_selector_type:
class: Your\Bundle\Form\Type\PasswordSelectorType
arguments: ["@security.context"]
tags:
- { name: form.type, alias: password_selector_type }
对于表单部分,您应该看看这个小部件。
http://symfony.com/doc/current/reference/forms/types/repeated.html
它提供了一种询问和处理字段确认的简单方法(当您将类型设置为密码时,它还会用星号隐藏值)。
$builder->add('password', 'repeated', array(
'type' => 'password',
'invalid_message' => 'The password fields must match.',
'options' => array('attr' => array('class' => 'password-field')),
'required' => true,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password')));
它将检查第一个和第二个选项是否相等。如果是这种情况,那么您的表格将被视为有效。否则,将显示 invalid_message,第一个字段将设置为用户键入的内容,同时确认字段(第二个选项)将被清空。
您可以在之后添加一些逻辑,例如对密码进行哈希处理以最终保留您的实体。 (在表单处理程序中提取它是一个很好的做法)。
这是我目前想到的,但我对该解决方案不满意,因为它涉及控制器中的自定义表单处理。然而,到目前为止,这是我找到的唯一方法。
我的表单 class 为用户密码添加了一个未映射的字段:
class UserType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('username')
->add('displayName')
->add('password', 'password', ['mapped' => false]);
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array('data_class' => 'MyCompany\MyBundle\Entity\User'));
}
public function getName() {
return 'user';
}
}
然后在我的控制器中手动处理此字段class:
class UserAdminController extends Controller {
public function editUserAction($userId, Request $request) {
$user = $this->getDoctrine()->getRepository('MyCompanyMyBundle:User')->findOneById($userId);
$form = $this->createForm('user', $user);
$form->handleRequest($request);
if ($form->isValid()) {
$newPassword = $form['password']->getData();
if ($newPassword !== "") {
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($newPassword, $user->getSalt());
$user->setPassword($password);
}
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
}
return $this->render(
"MyCompanyMyBundle:Admin/Management/User:Edit.html.php",
[
"form" => $form->createView()
]
);
}
}