문제

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

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top