Symfony4: Password Changing Issues - symfony

I am stuck on how to change passwords by form submission in symfony - and I have no idea how to solve it, since it seems to behave differently.
So first of all - I am building a custom form, where a user enters their old password, and a new one twice. This is my FormType:
UserPasswordType.php
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
class UserPasswordType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//basically just a unmapped password field
->add('password_current', PasswordType::class, [
'label' => 'user.password.current',
'mapped' => false,
/*'constraints' => new UserPassword()*/
])
//RepeatedType to get a confirm password field
->add('password', RepeatedType::class, [
'type' => PasswordType::class,
'first_name' => 'new',
'first_options' => ['label' => 'user.password.new'],
'second_name' => 'confirm',
'second_options' => ['label' => 'user.password.confirm']
])
->add('save', SubmitType::class, ['label' => 'app.save']);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class
]);
}
}
And here is my controller:
/**
* #Route("/user/settings", name="app_user_settings", defaults={"_locale":"%locale%"})
* #IsGranted("ROLE_USER")
*/
public function user_settings(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
$user = $this->getUser();
$form = $this->createForm(UserPasswordType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
//earlier: $current = $request->get('password_current'); - but does not work anymore?
$current = $form['password']->getData();
if ($passwordEncoder->isPasswordValid($user, $current)) { //fails always?
$confirm = $form['password_confirm']->getData();
$user->setPassword($passwordEncoder->encodePassword($user, $confirm));
$em->persist($user);
$em->flush();
$this->addFlash('success', 'status.success.text');
return $this->redirectToRoute('app_user');
}
$this->addFlash('danger', 'status.danger.text');
}
return $this->render('user/settings/index.html.twig', ['form' => $form->createView()]);
}
So here are my problems:
for some reason $current = $request->get('password_current') stopped working - it always returns null
$passwordEncoder->isPasswordValid($user, $current) also stopped working - was fine earlier now it literally always returns false
the constraint UserPassword() in my formtype on password_current also keeps failing validation, even though the password is correct
the logged-in user who tries to change their password keeps getting kicked (logged) out when trying to change the password (the form gets reloaded first, then after any action he returns to the login) - but just when I check the old password - without checking the old password the user stays logged in and gets the success flash message
Sadly I am really confused right now, because most of my stuff worked yesterday. Anyone knows how to approach those issues? Thanks

Well, I finally found the solution. I mapped the form type to the entity. So whenever I enter a new password, my "old" password actually is the new one. This makes every validation fail.
So just make a unmapped form type like this:
FormType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('password_current', PasswordType::class, ['label' => 'user.password.current', 'constraints' => new UserPassword()])
->add('password', RepeatedType::class, ['type' => PasswordType::class, 'first_name' => 'new', 'first_options' => ['label' => 'user.password.new'], 'second_name' => 'confirm', 'second_options' => ['label' => 'user.password.confirm']])
->add('save', SubmitType::class, ['label' => 'app.save']);
}
/* Remove entire function
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class
]);
}*/
Controller
//remove $user in createForm function
//$form = $this->createForm(UserPasswordType::class, $user);
$form = $this->createForm(UserPasswordType::class);
Funny side-effect. This also removes all other issues I had.

Related

Using a formtype to edit object

I am trying to use a formtype to edit one of my objects.
The formtype is working flawlessly when creating new objects, but when I try to load an existing entity into the form, I get the following exception:
Entity of type "Proxies__CG__\App\Entity\Language" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?
The object which gets put into the form is loaded from the database, so it is 100% persisted already.
Here are the types:
UnitType
class UnitType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('unitTranslations', CollectionType::class, [
'entry_type' => UnitTranslationType::class,
'entry_options' => array('label' => false),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'by_reference' => false,
'delete_empty' => true,
'attr' => [
'class' => 'UnitTranslationSelector',
],
])
->add('save', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Unit::class
));
}
}
UnitTranslationType
class UnitTranslationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, [
'label' => 'Unit',
'by_reference' => false
])
->add('language', EntityType::class, [
'class' => Language::class,
'choice_label' => 'name',
'placeholder' => 'Choose an option',
'by_reference' => false
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => UnitTranslation::class,
));
}
}
and the controller:
public function createUnit(Request $request, EntityManagerInterface $entityManager)
{
$unit = new Unit();
$unit = $entityManager->getRepository(Unit::class)->find(2);
dump($unit);
$form = $this->createForm(UnitType::class, $unit);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
foreach ($unit->getUnitTranslations() as $translation) {
$translation->setUnit($unit);
}
$entityManager->persist($unit);
$entityManager->flush();
return $this->redirectToRoute("recipe_list");
}
return $this->render('createUnit.html.twig', array('form' => $form->createView()));
}
I know this question is old, but I just solved this same problem by using CollectionType instead of EntityType (for the language entity in your case). I'm not sure how your entities are set up, but the CollectionType would require a OneToMany or ManyToMany relationship between UnitTranslation entity and Language entity. Description for CollectionType can be found here and how to embed a collection of forms can be found there.

Symfony - Form redirect

I am building simple car renting app in Symfony for a programming class at my university.
On URL /search I show a form to user. Right now when the form is submitted user his /search again but different template is rendered
This is form code:
class SearchQueryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('pickupCity', TextType::class)
->add('returnCity', TextType::class)
->add('pickupDateTime', DateTimeType::class, array(
'years' => range(2016,2017),
'error_bubbling' => true,
))
->add('returnDateTime', DateTimeType::class, array(
'years' => range(2016,2017),
'error_bubbling' => true,
))
->add('save', SubmitType::class, array(
'label' => 'Find Car',
'attr' => array(
'class' => 'btn-secondary btn-lg'
)))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\SearchQuery',
));
}
}
And controller for this route:
class SearchController extends Controller
{
public function searchAction(Request $request)
{
$query = new SearchQuery();
$form = $this->createForm(SearchQueryType::class, $query);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/* Some logic here */
return $this->render('AppBundle:default:results.html.twig', array(
'form' => $form->createView()
));
}
return $this->render('AppBundle:default:search.html.twig', array(
'form' => $form->createView()
));
}
}
However when the form is submitted I want to redirect to URL /results?SUBMITTED-PARMS-HERE, so user can send this link to someone and receive the same results. On /results again I wand to render form, this time filled with search params submitted and below render available car.
I don't know if its the best way to handle this problem, current solution works but link can't be send to someone else.
EDIT:
I change the code to fallowing:
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$session = $this->get('session');
$session->set('pickupCity', $data->getPickupCity());
$session->set('returnCity', $data->getReturnCity());
$session->set('pickupDateTime', $data->getPickupDateTime());
$session->set('returnDateTime', $data->getReturnDateTime());
return $this->redirectToRoute('results', array(
'pickupCity' => $data->getPickupCity(),
'returnCity' => $data->getReturnCity(),
'pickupDate' => $data->getPickupDateTime()->format('d-m-Y-H-i'),
'returnDate' => $data->getReturnDateTime()->format('d-m-Y-H-i'),
), 301);
}
So as a result I receive URL ./results/Paris/Berlin/10-02-2016-10-00/17-02-2016-10-00
And in routing.yml:
results:
path: /results/{pickupCity}/{returnCity}/{pickupDate}/{returnDate}
defaults: { _controller: AppBundle:Search:result}
methods: [POST]
Because I only want to receive POST request on this route and now when I submit form I get error page saying:
No route found for "GET /results/Paris/Berling/01-07-2016-00-00/01-11-2016-00-00": Method Not Allowed (Allow: POST)
Any idea how to fix it?
EDIT 2:
Never mind, my reasoning was bad.
Not sure, but maybe using this example.
$this->redirect($this->generateUrl('default', array('pickupCity' => $pickupCity, 'returnCity' => $returnCity, 'pickupDateTime' => $pickupDateTimepickupDateTime)));
With your own parameter, which you can get where you put your comment
/* Some logic here */
You can use getQueryString() function.
Sample:
if ($form->isSubmitted() && $form->isValid()) {
return $this->redirect('results?'.$request->getQueryString());
}

How to create a form to edit user roles with FriendsOfSymfony UserBundle

I'm trying to create a controller where I can edit the roles of a user (just that, nothing else) and I'm king of stuck.
I've created a form type:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'roles', 'choice', [
'choices' => ['ROLE_ADMIN', 'ROLE_USER', 'ROLE_CUSTOMER'],
'expanded' => true,
'multiple' => true,
]
)
->add('send', 'submit');
}
First, what would be the best way to retrieve the roles? Is there any way to associate a label to them?
In the controller I have this:
/**
* User role edition
*
* #Route(
* path="/edit-roles",
* name = "backoffice_user_edit_roles",
* requirements = {
* "id_user" = "\d*",
* },
* methods = {"GET"}
* )
*
* #Security("has_role('ROLE_ADMIN')")
*
* #Template
*/
public function editRolesAction($id_user)
{
$user = $this->user_repository->findOneById($id_user);
$form = $this->form_factory->create('dirital_user_roles_form_type', $user);
return [
'form' => $form->createView(),
'user' => $user
];
}
Problems that I have:
The form doesn't get populate with the current user roles, how should I do that?
When receiving the form, how can I update the user?
Thanks a lot
Actually it was easier than I thought – this is the form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'roles', 'choice', [
'choices' => ['ROLE_ADMIN' => 'ROLE_ADMIN', 'ROLE_USER' => 'ROLE_USER', 'ROLE_CUSTOMER' => 'ROLE_CUSTOMER'],
'expanded' => true,
'multiple' => true,
]
)
->add('save', 'submit', ['label' => 'ui.button.save']);
}
And the controller:
public function editRolesAction(Request $request, $id_user)
{
$user = $this->user_repository->findOneById($id_user);
$form = $this->form_factory->create('dirital_user_roles_form_type', $user);
$form->handleRequest($request);
if($form->isValid())
{
$this->addFlash('success', 'section.backoffice.users.edit_roles.confirmation');
$this->em->persist($user);
$this->em->flush();
$this->redirectToRoute('backoffice_user_edit_roles', ['id_user' => $user->getId()]);
}
return [
'form' => $form->createView(),
'user' => $user
];
}
The only part that remains to do is grabbing the form choices from the config instead of hardcoding them.
To rehuse easily in other controllers or actions, I like more this approach:
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\CallbackTransformer;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add(
'roles', 'choice', [
'choices' => ['ROLE_ADMIN' => 'ROLE_ADMIN', 'ROLE_USER' => 'ROLE_USER', 'ROLE_CUSTOMER' => 'ROLE_CUSTOMER'],
'expanded' => true,
'multiple' => true,
]
)
->add('save', 'submit', ['label' => 'ui.button.save']);
;
// Data transformer
$builder->get('roles')
->addModelTransformer(new CallbackTransformer(
function ($rolesArray) {
// transform the array to a string
return count($rolesArray)? $rolesArray[0]: null;
},
function ($rolesString) {
// transform the string back to an array
return [$rolesString];
}
));
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
without touching the controller.
I added the whole file to see the clases you need to import

Symfony2 FormBuilder with Entity class

I have a form that works well, there is just one issue with it and I'm hoping that I'll get an answer on how to do what I need to do.
<?php
namespace ADS\UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Security\Core\SecurityContext;
class UserType extends AbstractType {
private $type;
public function __construct($type) {
$this->type = $type;
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('firstName', 'text', array('required' => true))
->add('lastName', 'text', array('required' => true));
$builder->add('email', 'email', array('required' => true));
$builder->add('parentCompany', 'entity', array(
'class' => 'ADSUserBundle:Company',
'expanded' => false,
'empty_value' => 'CHOOSE ONE',
'required' => false,
'property' => 'companyName'
))
->add('enabled', 'choice', array('choices' => array('1' => 'Enabled', '0' => 'Disabled')))
->add('roles', 'entity', array(
'class' => 'ADSUserBundle:Roles',
'required' => true,
'property' => 'displayName',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array('data_class' => 'ADS\UserBundle\Entity\User'));
}
public function getName() { return 'ads_userbundle_user'; }
}
I have this form, the portion I am looking at is the 'roles' portion... Right now it created a multiple select box ( as I expect it to ), though the value is sequentially ie: 0,1,2,3,4...
What I really need is to figure out how to take this entity, and make the property to be the displayName ( as it is now ) and get the value to be the corresponding internalName This way it'll give me an array like:
array('ROLE_EXAMPLE' => 'EXAMPLE', 'ROLE_EXAMPLE1' => 'EXAMPLE1')
Any ideas how to accomplish this is greatly appreciated.
Kamil Adryjanek is correct, it is going to be much easier if you change it from an entity to a choice field. I've done some testing, both with FOSUserBundle and without the bundle - in both cases I hit some interesting road blocks.
First, I tried to run it through QueryBuilder in a repository, that didn't work out as it should have. The reason being, the fact that you wanted to be returning an array instead of a ORM object causes an error.
So next, I started looking at creating the choice field. All the guides, say to use the fieldname role instead of roles so I tried that, but I then had to duplicate the UserInterface from FOSUserBundle - I didn't want to do that -- so here I am stressed, and trying to figure it out.
Here is what I ended up doing, which works well.
private $normalRoles = array();
then in the __construct I add: $this->normalRoles = $roles;
Here is the builder:
$builder
->add('roles', 'choice', array(
'multiple' => true,
'choices' => $this->normalRoles
))
;
Originally, I left the multiple part out, figuring that it'd at least let me see an option box. I ended up getting an Array to String conversion error. So, adding the 'multiple' => true in, fixes that error.
Then, in my repository I created a function called normalizeRoles
public function normalizeRoles() {
$data = array();
$qb = $this->getEntityManager();
$query = $qb->createQuery(
"SELECT r.internalName, r.displayName FROM AcmeUserBundle:Roles r"
)->getArrayResult();
foreach ($query as $k => $v) {
$data[$v['internalName']] = $v['displayName'];
}
return $data;
}
From here, we just have to make some small edits in the DefaultController of the UserBundle in the newAction and editAction ( both are the same changes )
So, first off is to put into your Controller use Acme/UserBundle/Entity/Roles in order to avoid any errors and be able to get that repository.
Next, right before you create the form you run the normalizeRoles() function
$roles = $em->getRepository('AcmeUserBundle:Roles')->normalizeRoles()
Then, you pass it through the construct via: new UserType($roles)
full line for that would look like this:
$form = $this->createForm(new UserType($roles), $entity, array(
'action' => $this->generateUrl('acmd.user.edit', array(
'id' => $id)
)
));
or for new:
$form = $this->createForm(new UserType($roles), $entity, array(
'action' => $this->generateUrl('acmd.user.new')
)
));
At this point -- You'll have a working system that will allow you to dynamically add roles into a database table, and then associate those with a new or current user.
You can try do it via query_builder attribute:
$builder->add('roles', 'entity', array(
'class' => 'ADSUserBundle:Roles',
'required' => true,
'property' => 'displayName',
'query_builder' => function (RolesRepository $queryBuilder) {
return $queryBuilder->someMethod() // some method in this repository that return correct query to db.
},
));
In this case it would be better to use choice field Type (http://symfony.com/doc/current/reference/forms/types/choice.html) instead of entity and pass some role choices as option to form because entity field Type get entity id as key for choices:
public function buildForm(FormBuilderInterface $builder, array $options) {
...
$builder->add('roles', 'choice', array(
'choices' => $options['role_choices']
));
...
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'ADS\UserBundle\Entity\User',
'role_choices' => array()
));
}
Notice: it's recommended to pass variables to form through options parameter, not in constructor.
if I understand your question correctly, you need a data transformers. They help you to show data in form as you want.
Documentation: http://symfony.com/doc/current/cookbook/form/data_transformers.html

Avoid empty password field when edit a specific user with SonataAdminBundle

I have a problem when I want to edit an existing user from the Backend (using SonataAdminBundle and FOSUserBundle). In configureFormFields method of my UserAdmin class, the password field appears empty and this is a problem when I need to edit another fields (for example the lastname) keeping the same user password. This field (and the password verification field) must be filled again! (I do not want modify the user password)
In my UserAdmin class, I have:
public function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('User Data')
->add('username')
->add('plainPassword', 'repeated', array(
'type' => 'password',
'options' => array('translation_domain' => 'FOSUserBundle'),
'first_options' => array('label' => 'form.password'),
'second_options' => array('label' => 'form.password_confirmation'),
'invalid_message' => 'fos_user.password.mismatch',
))
->add('firstname')
->add('lastname')
->add('email')
->add('user_roles')
->add('enabled', 'checkbox', array(
'label' => 'Enable Account',
'required' => false,
))
->end()
;
}
I tried to overwrite prePersist and preUpdate methods in my UserAdmin class, but these do not work. The password is encripted in the database following the FOS standard (with salt and sha512).
Any solution?
Many thanks!
You can override your the preUpdate function in your admin class this is how i have done
public function preUpdate($object)
{
$DM = $this->getConfigurationPool()->getContainer()->get('Doctrine')->getManager();
$repository = $DM->getRepository('Namespace\YourBundle\Entity\User')->find($object->getId());
$Password = $object->getPassword();
if (!empty($Password)) {
$salt = md5(time());
$encoderservice = $this->getConfigurationPool()->getContainer()->get('security.encoder_factory');
$encoder = $encoderservice->getEncoder($object);
$encoded_pass = $encoder->encodePassword($object->getPassword(), $salt);
$object->setSalt($salt);
$object->setPassword($encoded_pass);
} else {
$object->setPassword($repository->getPassword());
}
}
And my configureFormFields function
protected function configureFormFields(FormMapper $formMapper)
{
$passwordoptions=array('type' => 'password','invalid_message' => 'The password fields must match.',
'options' => array('attr' => array('class' => 'password-field')),'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Confirm password'),'translation_domain' => 'FOSUserBundle'
);
$this->record_id = $this->request->get($this->getIdParameter());
if (!empty($this->record_id)) {
$passwordoptions['required'] = false;
$passwordoptions['constraints'] = array(new Assert\Length(array('min' => 10))
,new Assert\Regex(array('pattern' => '/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!#$%^&*-]).{10,}$/','match'=>true,'message'=>'Password must contain atleast 1 special character 1 upper case letter 1 number and 1 lower case letter !'))
);
} else {
$passwordoptions['required'] = true;
$passwordoptions['constraints'] = array(new Assert\Length(array('min' => 10))
,new Assert\Regex(array('pattern' => '/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!#$%^&*-]).{10,}$/','match'=>true,'message'=>'Password must contain atleast 1 special character 1 upper case letter 1 number and 1 lower case letter !'))
);
}
$formMapper->add('password', 'repeated', $passwordoptions); /*you can add your other fields*/
}
I have the same problem than you but without SonataAdminBundle.
In a classic user admin management (Symfony3), this works :
// src/AppBundle/Form/UserType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// By default, password is required (create user case)
$passwordOptions = array(
'type' => PasswordType::class,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat password'),
'required' => true
);
// If edit user : password is optional
// User object is stored in $options['data']
$recordId = $options['data']->getId();
if (!empty($recordId)) {
$passwordOptions['required'] = false;
}
$builder
->add('prenom', TextType::class, array('label' => 'First name'))
->add('nom', TextType::class, array('label' => 'Last name'))
->add('email', EmailType::class)
->add('username', TextType::class)
->add('plainPassword', RepeatedType::class, $passwordOptions)
->add('roleList', ChoiceType::class, array(
'label' => 'Role',
'choices' => array(
'Admin' => 'ROLE_ADMIN',
'User' => 'ROLE_USER'
),
))
->add('save', SubmitType::class, array(
'attr' => array('class' => 'button-link save'),
'label' => 'Validate'
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User',
));
}
}
Don't forget to remove #Assert\NotBlank() on plainPassword in your User Entity :
/**
* #Assert\Length(max=4096)
*/
private $plainPassword;
My editAction() in UserController.php :
/**
* #Route("/users/edit/{id}", name="user_edit")
*/
public function editAction($id, Request $request) {
// get user from database
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('AppBundle:User')->find($id);
// user doesn't exist
if (!$user) {
throw $this->createNotFoundException('No user found for id '. $id);
}
// build the form with user data
$originalPassword = $user->getPassword(); # encoded password
$form = $this->createForm(UserType::class, $user);
// form POST
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Encode the password if needed
# password has changed
if (!empty($form->get('plainPassword')->getData())) {
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
# password not changed
} else {
$user->setPassword($originalPassword);
}
$role = $form->get('roleList')->getData();
$user->setRoles(array($role));
// update the User
$em->flush();
// success message
$this->addFlash('notice', 'User has been updated successfully !');
// redirection
return $this->redirectToRoute('user');
}
// show form
return $this->render('users/form.html.twig', array(
'h1_title' => 'Edit user : ' . $user->getPrenom() . " " . $user->getNom(),
'form' => $form->createView()
));
}
Now, password is required when you create a new user, but it isn't when you edit one.
Maybe too late but useful for others.
With thanks to other posts.
So simple. Just use :
class UserAdmin extends AbstractAdmin
{
protected $formOptions = array(
'validation_groups' => array('Profile')
);
//..
}
It uses the profile validation group defined in:
friendsofsymfony/user-bundle/Resources/config/validation.xml
There are some ways to do that
Change configureFormField behavoir
You could get the current object ($this->subject or $this->getSubject();) inside the configureFormFields and check if its a new Object or an existing one and change the fields behaviour (validation for example)
Using saveHooks and FOSUser
here is an example
http://sonata-project.org/bundles/admin/master/doc/reference/saving_hooks.html
this will show the hashed password inside the password field but should update it when entering a new plain one ( if i remember correctly)
Combine both and implement your own logic
inside of the hook you can get the FOSUserManager
and handle the user update with that
container : $this->getConfigurationPool()->getContainer();
fosusermanager: $userManager = $container->get('fos_user.user_manager');
https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/user_manager.md

Resources