bugfix> php > 投稿

前書き

私は、ユーザー名の値によって管理されるパスワードのリセットフォームを作成する方法を見つけようとしています。

エラー

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
}

期待されるもの

ユーザーオブジェクトを新しいパスワードで更新します。

私のコード

ForgotController.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()));
            }
        }

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' %}

データベースに新しいユーザーを挿入しようとしていない限り、電子メールが言及されている理由はわかりませんが、コントローラーに基づいてそれをしようとしてはいけませんか?ユーザー名で識別されるパスワードを忘れた場合のフォームを追加するにはどうすればよいですか?

回答 2 件
  • パスワード変更フォームには2つのフィールドしか必要ないため、ユーザーエンティティの代わりに配列を使用します。 ChangePasswordTypeを少し調整する必要があります。

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

    動作を忘れたアクションは次のとおりです。

       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が注入され、すべての教義上のナンセンスが取り除かれます。ここで1つ注意すべき点があります。

    userInfo配列を作成し、フォーム処理に任せます。必要がない場合は、リクエストオブジェクトから属性を直接取得することに煩わされたくありません。

    次に、実際のユーザーエンティティを取得して更新します。 findByの代わりにfindOneByを使用していることに注意してください。ユーザー名が有効であることを確認します。本当に空想を得たい場合は、フォームに検証制約を追加して、このチェックを自動的に行うことができます。

    私はすべてのtry/catchのものを取り除きました。コードが乱雑になります。この時点までに例外がスローされた場合、それは本当に例外的であり、デフォルトの例外ハンドラーで処理できます。

    パスワードエンコーダーはまさにぴったりです。

    そして、$entityManager->の代わりに flush()$userRepository->を使用しました 流す();すぐに使用できるリポジトリにはフラッシュメソッドがないため、追加する必要があります。

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

    個人的には、エンティティマネージャではなくリポジトリだけを扱うのが好きです。しかし、必要に応じて、戻ってリポジトリの代わりにマネージャーを挿入することができます。あなたの電話。

    また、コメントで述べたように、ユーザーが他のユーザーのパスワードを変更できないようにセキュリティを追加する必要があります。

  • 以下の行に沿って何かを実装します-テンプレートやルーティングのような部分を残しました。これはあなたを助けるためだけです。

    フォーム1:ForgottenUserType -ユーザー名/メールのみを入力して送信

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add("username", null, array(
            "label" => "Email",
            "attr" => array(
                "class" => "form-control",
                "id" => "basic-url",
                "placeholder" => "Email address for your account"
            ),
            "constraints" => array(
                new Email(array("message" => "Invalid Email"))
            )
        ));
    }
    
    

    フォーム2:ChangePasswordFormType -ユーザーが新しいパスワードを入力して繰り返します。

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm($builder, $options);
        $builder
            ->add('plainPassword', RepeatedType::class, array(
                'type'              => PasswordType::class,
                'required'          => false,
                'first_options'     => array('label' => 'New password'),
                'second_options'    => array('label' => 'Confirm new password'),
                'invalid_message' => 'The password fields must match.',
            ))
            ;
    }
    
    

    コントローラー:ResetPasswordController -フォーム1のユーザールックアップリクエストとフォーム2のパスワードリセットリクエストを処理します。

    <?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 ResetPasswordController extends Controller
    {
        /**
         * Action for when a user has forgotten their password, to request ForgottenUser form
         *
         * @param Request $request
         */
        public function requestAction(Request $request)
        {
            $tmpUser = new User();
            $entityManager = $this->getDoctrine()->getManager();
            $form = $this->createForm(ForgottenUserType::class, $tmpUser);
            $form->handleRequest($request);
            if ($form->isSubmitted() && $form->isValid()) {
                $user = $entityManager->getRepository(User::class)->findBy(['username' => $tmpUser->getUsername()]);
                if ($user) {
                    //check and set token
                    if (null === $user->getConfirmationToken()) {
                        /** @var $tokenGenerator TokenGeneratorInterface */
                        $token = md5(uniqid($user->getUsername(), true)); //some unique token (you can create a nicer token generator in standalone class with a service)
                        $user->setConfirmationToken($token);
                        $user->setPasswordRequestedAt(new \DateTime());
                        $em->persist($user);
                        $em->flush();)
                        $this->addFlash('Info', 'If user is found, you will receive an email with further instructions.');
                        //send email using swiftmailer & include url with token
                    }
    
                } else {
                    //return to requestAction.
                }
            }
            //request template contains the ForgottenUserType form
            return $this->render(":path/to/template:request.html.twig", array(
                "forgotten_form" => $form->createView()
            ));
        }
        /**
         * Reset user password.
         *
         * @param Request $request
         * @param $token
         */
        public function resetAction(Request $request, $token)
        {
            $entityManager = $this->getDoctrine()->getManager();
            $user = $entityManager->getRepository(User::class)->findBy(['confirmationToken' => $token]); 
            if (null === $user) {                        
                return new RedirectResponse($this->generateUrl('resetting_request')); //to requestAction above. / create route
            }        
            $form = $this->createForm(ChangePasswordFormType::class, $user);
            $form->handleRequest($request);
            if ($form->isSubmitted() && $form->isValid()) {
                $user->SetConfirmationToken(null);
                $user->setPasswordRequestedAt(null);
                $entityManager->persist($user);
                $entityManager->flush()
                $this->addFlash("success", "Your password has been reset, log in now.");
                $url = $this->generateUrl('app.login'); //route to login page
                $response = new RedirectResponse($url);
                return $response;            
            }
            //reset template contains the ChangePasswordFormType form
            return $this->render(':path/to/forgottenpasswordtemplate:reset.html.twig', array(
                'token' => $token,
                'form' => $form->createView(),
            ));
        }
    }
    
    

あなたの答え