Symfony 3. 4 Нераспознанное поле: emailCanonical - PullRequest
0 голосов
/ 20 декабря 2018

Я новичок в Symfony 3.4, и мне было поручено добавить функцию сброса пароля на существующий веб-сайт.Я следовал руководству по использованию friendsofsymfony / user-bundle и дошел до того, что отправляю запрос Json своему контроллеру, и он отвечает обратно:

{"code":500,"message":"Unrecognized field: emailCanonical"}

Строка, вызывающая проблему:

$user = $this->get('fos_user.user_manager')->findUserByUsernameOrEmail($username);

Это полный файл src / AppBundle / RestPasswordManagementController.php

<?php
namespace AppBundle\Controller;

use FOS\RestBundle\Controller\Annotations;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Routing\ClassResourceInterface;
use FOS\RestBundle\Controller\Annotations\RouteResource;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\Event\GetResponseNullableUserEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;

/**
 * @Annotations\Prefix("password")
 * @RouteResource("/password", pluralize=false)
 */
class RestPasswordManagementController extends FOSRestController implements ClassResourceInterface
{

    /**
     * @Annotations\Post("/reset/request")
     */
    public function requestResetAction(Request $request)
    {

        $username = $request->get('email');        

        /** @var $user UserInterface */
        $user = $this->get('fos_user.user_manager')->findUserByUsernameOrEmail($username);

            return new JsonResponse(
                $user,
                JsonResponse::HTTP_FORBIDDEN
            );

        /** @var $dispatcher EventDispatcherInterface */
        $dispatcher = $this->get('event_dispatcher');
        /* Dispatch init event */
        $event = new GetResponseNullableUserEvent($user, $request);
        $dispatcher->dispatch(FOSUserEvents::RESETTING_SEND_EMAIL_INITIALIZE, $event);
        if (null !== $event->getResponse()) {
            return $event->getResponse();
        }
        if (null === $user) {
            return new JsonResponse(
                'User not recognised',
                JsonResponse::HTTP_FORBIDDEN
            );
        }
        $event = new GetResponseUserEvent($user, $request);
        $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_REQUEST, $event);
        if (null !== $event->getResponse()) {
            return $event->getResponse();
        }
        if ($user->isPasswordRequestNonExpired($this->container->getParameter('fos_user.resetting.token_ttl'))) {
            return new JsonResponse(
                $this->get('translator')->trans('resetting.password_already_requested', [], 'FOSUserBundle'),
                JsonResponse::HTTP_FORBIDDEN
            );
        }
        if (null === $user->getConfirmationToken()) {
            /** @var $tokenGenerator \FOS\UserBundle\Util\TokenGeneratorInterface */
            $tokenGenerator = $this->get('fos_user.util.token_generator');
            $user->setConfirmationToken($tokenGenerator->generateToken());
        }
        /* Dispatch confirm event */
        $event = new GetResponseUserEvent($user, $request);
        $dispatcher->dispatch(FOSUserEvents::RESETTING_SEND_EMAIL_CONFIRM, $event);
        if (null !== $event->getResponse()) {
            return $event->getResponse();
        }
        $this->get('fos_user.mailer')->sendResettingEmailMessage($user);
        $user->setPasswordRequestedAt(new \DateTime());
        $this->get('fos_user.user_manager')->updateUser($user);
        /* Dispatch completed event */
        $event = new GetResponseUserEvent($user, $request);
        $dispatcher->dispatch(FOSUserEvents::RESETTING_SEND_EMAIL_COMPLETED, $event);
        if (null !== $event->getResponse()) {
            return $event->getResponse();
        }
        return new JsonResponse(
            $this->get('translator')->trans(
                'resetting.check_email',
                [ '%tokenLifetime%' => floor($this->container->getParameter('fos_user.resetting.token_ttl') / 3600) ],
                'FOSUserBundle'
            ),
            JsonResponse::HTTP_OK
        );
    }
    /**
     * Reset user password
     * @Annotations\Post("/reset/confirm")
     */
    public function confirmResetAction(Request $request)
    {
        $token = $request->request->get('token', null);
        if (null === $token) {
            return new JsonResponse('You must submit a token.', JsonResponse::HTTP_BAD_REQUEST);
        }
        /** @var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
        $formFactory = $this->get('fos_user.resetting.form.factory');
        /** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */
        $userManager = $this->get('fos_user.user_manager');
        /** @var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
        $dispatcher = $this->get('event_dispatcher');
        $user = $userManager->findUserByConfirmationToken($token);
        if (null === $user) {
            return new JsonResponse(
            // no translation provided for this in \FOS\UserBundle\Controller\ResettingController
                sprintf('The user with "confirmation token" does not exist for value "%s"', $token),
                JsonResponse::HTTP_BAD_REQUEST
            );
        }
        $event = new GetResponseUserEvent($user, $request);
        $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_INITIALIZE, $event);
        if (null !== $event->getResponse()) {
            return $event->getResponse();
        }
        $form = $formFactory->createForm([
            'csrf_protection'    => false,
            'allow_extra_fields' => true,
        ]);
        $form->setData($user);
        $form->submit($request->request->all());
        if (!$form->isValid()) {
            return $form;
        }
        $event = new FormEvent($form, $request);
        $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_SUCCESS, $event);
        $userManager->updateUser($user);
        if (null === $response = $event->getResponse()) {
            return new JsonResponse(
                $this->get('translator')->trans('resetting.flash.success', [], 'FOSUserBundle'),
                JsonResponse::HTTP_OK
            );
        }
        // unsure if this is now needed / will work the same
        $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
        return new JsonResponse(
            $this->get('translator')->trans('resetting.flash.success', [], 'FOSUserBundle'),
            JsonResponse::HTTP_OK
        );
    }
    /**
     * Change user password
     *
     * @ParamConverter("user", class="AppBundle:User")
     *
     * @Annotations\Post("/{user}/change")
     */
    public function changeAction(Request $request, UserInterface $user)
    {
        if ($user !== $this->getUser()) {
            throw new AccessDeniedHttpException();
        }
        /** @var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
        $dispatcher = $this->get('event_dispatcher');
        $event = new GetResponseUserEvent($user, $request);
        $dispatcher->dispatch(FOSUserEvents::CHANGE_PASSWORD_INITIALIZE, $event);
        if (null !== $event->getResponse()) {
            return $event->getResponse();
        }
        /** @var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
        $formFactory = $this->get('fos_user.change_password.form.factory');
        $form = $formFactory->createForm([
            'csrf_protection'    => false
        ]);
        $form->setData($user);
        $form->submit($request->request->all());
        if ( ! $form->isValid()) {
            return $form;
        }
        /** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */
        $userManager = $this->get('fos_user.user_manager');
        $event = new FormEvent($form, $request);
        $dispatcher->dispatch(FOSUserEvents::CHANGE_PASSWORD_SUCCESS, $event);
        $userManager->updateUser($user);
        if (null === $response = $event->getResponse()) {
            return new JsonResponse(
                $this->get('translator')->trans('change_password.flash.success', [], 'FOSUserBundle'),
                JsonResponse::HTTP_OK
            );
        }
        $dispatcher->dispatch(FOSUserEvents::CHANGE_PASSWORD_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
        return new JsonResponse(
            $this->get('translator')->trans('change_password.flash.success', [], 'FOSUserBundle'),
            JsonResponse::HTTP_OK
        );
    }
}

Форма longin не использует тот же Bundle.На всякий случай это может помочь, вот файл: src / AppBundle / Security / LoginFormAuthenticator.php

<?php

namespace AppBundle\Security;

use AppBundle\Form\LoginForm;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
    private $formFactory;
    private $em;
    private $router;
    private $passwordEncoder;

    public function __construct(FormFactoryInterface $formFactory, EntityManager $em, RouterInterface $router, UserPasswordEncoder $passwordEncoder)
    {
        $this->formFactory = $formFactory;
        $this->em = $em;
        $this->router = $router;
        $this->passwordEncoder = $passwordEncoder;
    }

    public function getCredentials(Request $request)
    {
        $isLoginSubmit = $request->getPathInfo() == '/login' && $request->isMethod('POST');
        if (!$isLoginSubmit) {
            // skip authentication
            return;
        }

        $form = $this->formFactory->create(LoginForm::class);
        $form->handleRequest($request);

        $data = $form->getData();
        $request->getSession()->set(
            Security::LAST_USERNAME,
            $data['_username']
        );

        return $data;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $username = $credentials['_username'];

        return $this->em->getRepository('AppBundle:User')
            ->findOneBy(['email' => $username]);
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        $password = $credentials['_password'];

        if ($this->passwordEncoder->isPasswordValid($user, $password)) {
            return true;
        }

        return false;
    }

    protected function getLoginUrl()
    {
        return $this->router->generate('security_login');
    }

    protected function getDefaultSuccessRedirectUrl()
    {
        return $this->router->generate('app-dashboard');
    }
}

Что касается представления, то оно довольно простое.Существует сценарий jQuery, который переключается между формой входа и утерянным паролем.

{% extends '::base.html.twig' %}

{# -- PAGE TITLE --  #}
{% block title %}
    {{ title }}
{% endblock %}

{# -- PAGE CONTENT --  #}
{% block body %}

    <div class="account-pages"></div>
    <div class="clearfix"></div>
    <div class="wrapper-page">

        <div class="account-bg">
            <div class="card-box m-b-0">
                <div class="text-xs-center">
                    <a href="/" class="logo">
                        <img src="{{ asset('assets/images/app/login-logo.png') }}" alt="SSTenligne">
                    </a>
                </div>

                <div class="m-t-10 p-20 login_form">

                    {{ form_start(form, {'autocomplete': 'off'}) }}
                        {{ form_row(form._username) }}
                        {{ form_row(form._password) }}

                        <div class="form-group text-center row m-t-30">
                            <div class="col-xs-12">
                                <button class="btn btn-primary btn-block waves-effect waves-light" type="submit">{{ 'Login'|trans }}</button>
                            </div>
                        </div>
                    {{ form_end(form) }}

                    <a class="mdp_resset" href="#">Mot de passe perdu?</a>

                </div>

                <div class="m-t-10 p-20 password_resset">

                    {{ form_start(form2, {'autocomplete': 'off'}) }}
                        {{ form_row(form2._username_resset) }}

                        <div class="form-group text-center row m-t-30">
                            <div class="col-xs-12">
                                <button class="btnResset btn btn-primary btn-block waves-effect waves-light" type="submit">{{ 'Récupération'|trans }}</button>
                            </div>
                        </div>
                    {{ form_end(form2) }}

                    <a class="mdp_login" href="#">Retour</a>

                </div>

                <div class="clearfix"></div>
            </div>
        </div>
        <!-- end card-box-->

    </div>
    <!-- end wrapper page -->

{% endblock %}


{# -- SPECIFIC SCRIPTS --  #}
{% block scripts %}

    <script>
        {% if error %}
            toastr.error('{{ error.messageKey|trans(error.messageData, 'security') }}');
        {% endif %}

        $(".password_resset form").submit(function(e){
            e.preventDefault();

            $.ajax({
                type: "POST",
                dataType: "JSON",
                url: "password/reset/request",
                data:{
                    email:$("#resset_form__username_resset").val()
                },
                error: function (jql, code, returnMsgError) {
                    alert('Une erreur est survenue');
                },
                success: function ($data) {
                    alert('It\'s posted!');
                }
            });
        });

    </script>
{% endblock %}

Запрос отправлен на пароль / сброс / запрос, и там я получаю сообщение об ошибке:

{"code ": 500," message ":" Нераспознанное поле: emailCanonical "}

Я новичок в Symfony и не знаю, с чего начать отладку этой проблемы.Любая помощь будет высоко ценится.Если вам нужно, чтобы я показал больше кода, чтобы помочь мне найти решение, оставьте меня в курсе, и я отредактирую.

РЕДАКТИРОВАТЬ: Поэтому, как указано в комментариях, я начал реализовывать пользовательский Canonicalizer

В моем services.yml я добавил

app.my_canonicalizer:
    class: AppBundle\Util\MyCanonicalizer
    arguments: ['@normalize_service']
    public: false

В своем конфиге я добавил

# FOS User
fos_user:
    db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
    firewall_name: api
    user_class: AppBundle\Entity\User
    from_email:
        address:        "[my_email]"
        sender_name:    "[my_name]"
    service:
        mailer: user.mailer.rest
        email_canonicalizer:    app.my_canonicalizer
        username_canonicalizer: app.my_canonicalizer

, а затем создал файлы с именем: AppBundle / Util / MyCanonicalizer.php

<?php
namespace AppBundle\Util;

use FOS\UserBundle\Util\CanonicalizerInterface;

class MyCanonicalizer implements CanonicalizerInterface
{
    private $normalizeService;

    public function __construct(NormalizeService $normalizeService)
    {
        $this->normalizeService = $normalizeService;
    }

    public function canonicalize($string)
    {
        if (null === $string) {
            return null;
        }

        return $this->normalizeService->normalize($string);
    }
}
?>

И когда я отправляю запрос, я получаю эту ошибку:

<b>Fatal error</b>:  Uncaught Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: The service &quot;fos_user.util.email_canonicalizer&quot; has a dependency on a non-existent service &quot;normalize_service&quot;. in /home/website/public_html/intranet/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php:31
Stack trace:
#0 /home/website/public_html/intranet/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php(60): Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass-&gt;processValue(Object(Symfony\Component\DependencyInjection\Reference), false)
#1 /home/website/public_html/intranet/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php(28): Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass-&gt;processValue(Array, false)
#2 /home/website/public_html/intranet/vendor/sym in <b>/home/website/public_html/intranet/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php</b> on line <b>31</b><br />
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...