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

tre*_*ake 4 php symfony symfony4

介绍

我一直在试图弄清楚如何创建一个由用户名值控制的重置密码表单。

错误

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
}
Run Code Online (Sandbox Code Playgroud)

什么预期

使用新密码更新我的用户对象。

我的代码

忘记控制器.php

我知道这可能不是获取密码的正确方法,但是搜索 Symfony 4 忘记密码表单会显示与我的版本无关的 symfony2.4 帖子

    <?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()));
            }
        }
Run Code Online (Sandbox Code Playgroud)

更改密码类型.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
            ));
        }
    }
Run Code Online (Sandbox Code Playgroud)

忘记.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' %}
Run Code Online (Sandbox Code Playgroud)

我不确定为什么会提到电子邮件,除非它试图将新用户插入数据库,但它不应该根据我的控制器尝试这样做?如何添加由用户名标识的忘记密码表单?

Cer*_*rad 6

由于您的更改密码表单只需要两个字段,我们将使用数组而不是用户实体。需要对 ChangePasswordType 稍作调整:

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

这是一个有效的忘记操作:

    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()));
    }
Run Code Online (Sandbox Code Playgroud)

UserRepository 被注入,它摆脱了所有的学说废话。这里有一个警告,我会回到这里。

我们构建 userInfo 数组并让表单处理完成它的工作。如果不需要,我们真的不想直接从请求对象中获取属性。

然后我们让我们的实际用户实体更新。请注意使用 findOneBy 而不是 findBy。我们检查以确保用户名有效。如果您真的想要花哨,那么您可以向表单添加验证约束以自动执行此检查。

我摆脱了所有的 try/catch 东西。它只会弄乱你的代码。此时,如果抛出异常,则它确实是异常的,并且可以由默认异常处理程序处理。

你得到的密码编码器的东西恰到好处。

然后我用 $userRepository->flush() 代替 $entityManager->flush(); 开箱即用的存储库上没有刷新方法,因此您需要添加一个:

// UserRepository
public function flush()
{
    $this->_em->flush();
}
Run Code Online (Sandbox Code Playgroud)

我个人喜欢只处理存储库而不是实体管理器。但是如果需要,您可以返回并注入管理器而不是存储库。您的来电。

正如评论中提到的,您确实希望添加一些安全性以防止用户更改其他用户的密码。