douyoupingji7238 2018-05-25 13:15
浏览 92
已采纳

Symfony 4按用户名更改密码 - 电子邮件不能为空

Introduction

I have been trying to figure out how to create a reset password form that's governed by username value.

The Error

Path        Message                           Invalid value     Violation
data.email  This value should not be blank.   null 



ConstraintViolation {#945 ▼
  -message: "This value should not be blank."
  -messageTemplate: "This value should not be blank."
  -parameters: [▶]
  -plural: null
  -root: Form {#620 ▶}
  -propertyPath: "data.email"
  -invalidValue: null
  -constraint: NotBlank {#477 …}
  -code: "c1051bb4-d103-4f74-8988-acbcafc7fdc3"
  -cause: null
}

What's expected

Update my User Object with the new password.

My Code

ForgotController.php

I know this probably isn't the correct way to get the password, but searching Symfony 4 forgotten password form brings up symfony2.4 posts which aren't relevant to my version

    <?php
        namespace App\Controller\User;

        use App\Entity\User;
        use App\Form\User\ChangePasswordType;
        use App\Repository\UserRepository;
        use Symfony\Bundle\FrameworkBundle\Controller\Controller;
        use Symfony\Component\HttpFoundation\Request;
        use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

        class ForgotController extends Controller
        {
            public function forgot(Request $request, UserPasswordEncoderInterface $encoder)
            {
                $entityManager = $this->getDoctrine()->getManager();

                $changePassword = $request->request->get('change_password');

                $username = $changePassword['username'];
                $password = $changePassword['plainPassword']['first'];

                $user       = $entityManager->getRepository(User::class)->findBy(['username' => $username]);
                $userEntity = new User();

                if (!$user) {
                    $this->addFlash('danger', 'User not found for '. $username);
                }

                $form = $this->createForm(ChangePasswordType::class, $userEntity);
                $form->handleRequest($request);

                if ($form->isSubmitted() && $form->isValid()) {
                    try {
                        $pass = $encoder->encodePassword($userEntity, $password);

                        $userEntity->setPassword($pass);
                        $entityManager->flush();

                        $this->addFlash('success', 'Password Changed!');
                    } catch (Exception $e) {
                        $this->addFlash('danger', 'Something went skew-if. Please try again.');
                    }

                    return $this->redirectToRoute('login');
                }

                return $this->render('user/forgot.html.twig', array('form' => $form->createView()));
            }
        }

ChangePasswordType.php

<?php
    namespace App\Form\User;

    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\TextType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolver;

    class ChangePasswordType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->add('username', TextType::class)
                ->add('plainPassword', RepeatedType::class, array(
                'type' => PasswordType::class,
                'first_options' => array('label' => 'New Password'),
                'second_options' => array('label' => 'Repeat New Password')
            ));
        }

        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults(array(
                'data_class' => User::class
            ));
        }
    }

forgot.html.twig

{% include 'builder/header.html.twig' %}

<div class="user-container" id="user-content">
    {% block body %}
        {% include 'builder/notices.html.twig' %}

        <div class="user-container">
            <i class="fas fa-user-edit fa-5x"></i>
        </div>

        <hr />

        {{ form_start(form) }}
            {{ form_row(form.username, { 'attr': {'class': 'form-control'} }) }}
            {{ form_row(form.plainPassword.first, { 'attr': {'class': 'form-control'} }) }}
            {{ form_row(form.plainPassword.second, { 'attr': {'class': 'form-control'} }) }}

            <div class="register-btn-container">
                <button class="btn btn-danger" id="return-to-dash-btn" type="button">Cancel!</button>
                <button class="btn btn-primary" type="submit">Update!</button>
            </div>
        {{ form_end(form) }}
    {% endblock %}
</div>

{% include 'builder/footer.html.twig' %}

I'm not sure why email is even being mentioned unless it's trying to insert a new user into a database but it shouldn't be trying to do that based on my controller? How can I go about adding a forgot password form that's identified by username?

  • 写回答

2条回答 默认 最新

  • dongshi9407 2018-05-25 14:28
    关注

    Since your change password form only needs two fields we will use an array instead of a user entity. Need a slight tweak to ChangePasswordType:

        // ChangePasswordType
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults(array(
                //'data_class' => User::class
            ));
        }
    

    Here is a working forgot action:

        public function forgot(
            Request $request, 
            UserPasswordEncoderInterface $encoder, 
            UserRepository $userRepository)
        {
    
            $userInfo = ['username' => null, 'plainPassword' => null];
    
            $form = $this->createForm(ChangePasswordType::class, $userInfo);
            $form->handleRequest($request);
    
            if ($form->isSubmitted() && $form->isValid()) {
    
                $userInfo = $form->getData();
                $username = $userInfo['username'];
                $plainPassword = $userInfo['plainPassword'];
    
                $user = $userRepository->findOneBy(['username' => $username]);
                if ($user === null) {
                    $this->addFlash('danger', 'Invalid username');
                    return $this->redirectToRoute('forgot');
                }
                $password = $encoder->encodePassword($user, $plainPassword);
    
                $user->setPassword($password);
                $userRepository->flush();
    
                return $this->redirectToRoute('login');
            }
    
            return $this->render('user/forgot.html.twig', array('form' => $form->createView()));
        }
    

    UserRepository is injected which get's rid of all the doctrine nonsense. There is one caveat here that I'll get back to.

    We build the userInfo array and let the form processing do it's thing. We really don't want to mess with getting attributes directly from the request object if we don't have to.

    Then we get our actual user entity to update. Notice the use of findOneBy instead of findBy. We check to make sure username is valid. If you really wanted to get fancy then you could add a validation constraint to the form to do this check automatically.

    I got rid of all the try/catch stuff. It just clutters up your code. By this point if an exception is thrown then it is truly exceptional and can be handled by the default exception handlers.

    You got the password encoder stuff just right.

    And then instead of $entityManager->flush() I used $userRepository->flush(); Out of the box there is no flush method on repositories so you need to add one:

    // UserRepository
    public function flush()
    {
        $this->_em->flush();
    }
    

    I personally like dealing with just repositories and not the entity manager. But if want, you can just go back and inject the manager instead of the repository. Your call.

    And as mentioned in the comments, you do want to add some security to prevent users from changing other users passwords.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥80 部署运行web自动化项目
  • ¥15 腾讯云如何建立同一个项目中物模型之间的联系
  • ¥30 VMware 云桌面水印如何添加
  • ¥15 用ns3仿真出5G核心网网元
  • ¥15 matlab答疑 关于海上风电的爬坡事件检测
  • ¥88 python部署量化回测异常问题
  • ¥30 酬劳2w元求合作写文章
  • ¥15 在现有系统基础上增加功能
  • ¥15 远程桌面文档内容复制粘贴,格式会变化
  • ¥15 这种微信登录授权 谁可以做啊