In my Symfony2 application I created a custom form to edit objects of a User
class. Users have a password
property which contains a hash of the user's password. For obvious reasons, I do not want to echo this property's value into a field. However, I want a password field on my form so that when editing a user it is possible to change the user's password.
This password field should behave as follows:
********
. I thought of implementing this with a custom data transformer. However, the data transformer does not provide me with a way to skip updating the user's password
property when the password field is posted empty.
Where do I need to extend the framework to add custom logic deciding which fields should be updated?
This is the legacy code I am trying to replace:
/* 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);
}
I can live with removing the string length checks. All I want to check is whether something has been entered.
As you can see, I can not simply move this code to a data transformer as it needs to access the $user
which is the user we are currently editing. I don't think it is a good idea to create a service providing this value.
Just insert a control directly into your entity method and use data transformer (as you have insight)
So your entity will be
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
}
}
If you want to take advantage only of DataTransformers, you could still do what you need that way
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();
}
}
}
Of course you need to inject service_container
service into you data transformer (or better, you should inject it into your form type's selector and pass to DataTransformer constructor as follows:
services:
your.bundle.password_selector_type:
class: Your\Bundle\Form\Type\PasswordSelectorType
arguments: ["@security.context"]
tags:
- { name: form.type, alias: password_selector_type }
For the form part, you should take a look a this widget.
http://symfony.com/doc/current/reference/forms/types/repeated.html
It provides an easy way to ask and treat confirmation on a field (it also hide values with stars when you set type to password).
$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')));
It will check first and second options to be equal. If that's the case then your form will be considered as valid. Otherwise, invalid_message will be displayed and first field will set to the content typed by user while confirmation field (second option) will be emptied.
You can add some logic afterwards like hashing the password to finally persist your entity. (Extracting it in a form handler would be a good practice).
Here is what I came up with for now but I am not happy with the solution as it involves custom form processing in the controller. However, so far this is the only way I have found to make it work.
My form class adds an unmapped field for the user's password:
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';
}
}
This field is then processed manually in my controller 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()
]
);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.