Редактирование пользователя с ошибкой неверно изменяет имя пользователя app.user.username, как его решить?

Для наших пользователей мы используем инфраструктуру Symfony2 и FOSUserBundle. Итак, у нас есть собственный UserBundle, который наследуется от FOSUserBundle. Проблема заключается в следующем: когда мы отправляем форму для редактирования пользователя с неправильным паролем, имя app.user.us, которое отображается в заголовке, изменяется, когда оно не должно, поскольку форма неправильная. Затем, когда мы перезагружаем страницу, у app.user.username есть обычное имя пользователя (поскольку оно не изменилось в базе данных). Вопрос в том, почему app.user.username временно изменяется для значения формы (редактировать пользователя), которую мы только что отправили с неправильным паролем?

мы используем обычную маршрутизацию из FOSUser:

fos_user_security: resource: "@FOSUserBundle/Resources/config/routing/security.xml" fos_user_profile: resource: "@FOSUserBundle/Resources/config/routing/profile.xml" prefix: /profile fos_user_register: resource: "@FOSUserBundle/Resources/config/routing/registration.xml" prefix: /register fos_user_resetting: resource: "@FOSUserBundle/Resources/config/routing/resetting.xml" prefix: /resetting fos_user_change_password: resource: "@FOSUserBundle/Resources/config/routing/change_password.xml" prefix: /profile 

И контроллер по умолчанию из FOSUser:

 public function editAction(Request $request) { $user = $this->getUser(); if (!is_object($user) || !$user instanceof UserInterface) { throw new AccessDeniedException('This user does not have access to this section.'); } /** @var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */ $dispatcher = $this->get('event_dispatcher'); $event = new GetResponseUserEvent($user, $request); $dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_INITIALIZE, $event); if (null !== $event->getResponse()) { return $event->getResponse(); } /** @var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */ $formFactory = $this->get('fos_user.profile.form.factory'); $form = $formFactory->createForm(); $form->setData($user); $form->handleRequest($request); if ($form->isValid()) { /** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */ $userManager = $this->get('fos_user.user_manager'); $event = new FormEvent($form, $request); $dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_SUCCESS, $event); $userManager->updateUser($user); if (null === $response = $event->getResponse()) { $url = $this->generateUrl('fos_user_profile_show'); $response = new RedirectResponse($url); } $dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_COMPLETED, new FilterUserResponseEvent($user, $request, $response)); return $response; } return $this->render('FOSUserBundle:Profile:edit.html.twig', array( 'form' => $form->createView() )); } 

Вот часть кода, которая отображается в заголовке, который НЕ должен меняться при отправке формы с неправильным паролем. (App.user.username)

 <ul class="nav navbar-nav navbar-right"> {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %} <li> <a href="{{ path('fos_user_profile_show') }}">{{ app.user.username }} </a> </li> <li> <a href="{{ path('fos_user_security_logout') }}"> {{ 'layout.logout'|trans({}, 'FOSUserBundle') }} </a> </li> {% else %} <li> <a href="{{ path('fos_user_registration_register') }}">{{ 'layout.register'|trans({}, 'FOSUserBundle') }}</a> </li> <li> <a href="{{ path('fos_user_security_login') }}">{{ 'layout.login'|trans({}, 'FOSUserBundle') }}</a> </li> {% endif %} </ul> 

И вот код отображаемой формулы:

 <div class="well"> {{ form_start(form, {'attr': {'class': 'form-horizontal'}}) }} {{ form_errors(form) }} <div class="form-group"> {# Génération du label username. #} {{ form_label(form.username, 'register.form.username'|trans, {'label_attr': {'class': 'col-sm-3 control-label'}}) }} {# Affichage des erreurs pour ce champ précis. #} {{ form_errors(form.username) }} <div class="col-sm-4"> {{ form_widget(form.username, {'attr': {'class': 'form-control'}}) }} </div> </div> <div class="form-group"> {# Génération du label adresse. #} {{ form_label(form.address, 'register.form.address'|trans, {'label_attr': {'class': 'col-sm-3 control-label'}}) }} {# Affichage des erreurs pour ce champ précis. #} {{ form_errors(form.address) }} <div class="col-sm-4"> {{ form_widget(form.address, {'attr': {'class': 'form-control'}}) }} </div> </div> <div class="form-group"> {# Génération du label nom du contact. #} {{ form_label(form.contactName, 'register.form.contact_name'|trans, {'label_attr': {'class': 'col-sm-3 control-label'}}) }} {# Affichage des erreurs pour ce champ précis. #} {{ form_errors(form.contactName) }} <div class="col-sm-4"> {{ form_widget(form.contactName, {'attr': {'class': 'form-control'}}) }} </div> </div> <div class="form-group"> {# Génération du label email. #} {{ form_label(form.email, 'register.form.email'|trans, {'label_attr': {'class': 'col-sm-3 control-label'}}) }} {# Affichage des erreurs pour ce champ précis. #} {{ form_errors(form.email) }} <div class="col-sm-4"> {{ form_widget(form.email, {'attr': {'class': 'form-control'}}) }} </div> </div> <div class="form-group"> {# Génération du label numéro de téléphone. #} {{ form_label(form.phone, 'register.form.phone'|trans, {'label_attr': {'class': 'col-sm-3 control-label'}}) }} {# Affichage des erreurs pour ce champ précis. #} {{ form_errors(form.phone) }} <div class="col-sm-4"> {{ form_widget(form.phone, {'attr': {'class': 'form-control'}}) }} </div> </div> <div class="form-group"> {# Génération du label mot de passe. #} {{ form_label(form.current_password, 'register.form.password'|trans, {'label_attr': {'class': 'col-sm-3 control-label'}}) }} {# Affichage des erreurs pour ce champ précis. #} {{ form_errors(form.current_password) }} <div class="col-sm-4"> {{ form_widget(form.current_password, {'attr': {'class': 'form-control'}}) }} </div> </div> {# Pour le bouton, pas de label ni d'erreur, on affiche juste le widget #} {{ form_widget(form.saveButton, {'attr': {'class': 'btn btn-primary'}, 'label': 'profile.edit.submit'|trans }) }} {# Génération automatique des champs pas encore écrits. Dans cet exemple, ce serait le champ CSRF (géré automatiquement par Symfony !) et tous les champs cachés (type « hidden »). #} {{ form_rest(form) }} {# Fermeture de la balise <form> du formulaire HTML #} {{ form_end(form) }} </div> 

И вот форма FormType, которую мы используем для этой конкретной формы:

 public function buildForm(FormBuilderInterface $builder, array $options) { // add your custom field $builder->add('contactName', 'text') ->add('phone', 'text') ->add('address', 'text') ->add('saveButton', 'submit'); } 

FormType наследует от ProfileFormType (по умолчанию в FOSUserBundle) благодаря функции getParent ().

Благодарим вас за помощь в выяснении того, что не так. Мы не понимаем, почему имя app.user.username изменяется в заголовке, поскольку форма отправляется с неправильным паролем, а данные в базе данных не изменяются и извините за комментарии, которые находятся на французском языке в коде :).

Solutions Collecting From Web of "Редактирование пользователя с ошибкой неверно изменяет имя пользователя app.user.username, как его решить?"

Это происходит по дизайну. В настоящий момент вы вызываете $form->handleRequest($request) пользовательский объект обновляется с данными представленной формы. Когда вы app.user доступ к app.user вы фактически обращаетесь к одному и тому же пользовательскому объекту. В документах есть более подробная информация об этом

Это работает для меня:

1) Добавить свойства currentUsername и обратные вызовы жизненного цикла в класс пользовательских сущностей

 namespace AppBundle\Entity; use FOS\UserBundle\Model\User as BaseUser; use Doctrine\ORM\Mapping as ORM; /** * Class User * @package AppBundle\Entity * * @ORM\Entity(repositoryClass="AppBundle\Repositories\User") * @ORM\Table(name="site_user") * @ORM\HasLifecycleCallbacks() */ class User extends BaseUser { /** * @var int * * @ORM\Column(type="integer", nullable=false) * @ORM\Id() * @ORM\GeneratedValue() */ protected $id; /** * @var string */ public $currentUsername; /** * @ORM\PostLoad() * @ORM\PostUpdate() * @ORM\PostPersist() */ public function syncCurrentUsername() { $this->currentUsername = $this->username; } /** * @return string */ public function getCurrentUsername() { return $this->currentUsername; } } 

2) И затем в шаблоне используйте:

 {{ app.user.getCurrentUsername() }} 

3) В некоторых случаях, возможно, вам придется переопределить методы сериализации и десериализации и добавить к нему новое свойство (currentUsername).

Вы можете использовать FOSUserBundle-EventListener. (LoginListener | ProfileEditListener) Я сохраняю имя пользователя в контексте сеанса и распечатываю его в twig-template вместо app.user.username.

Он работает нормально.

 <?php // LoginListener.php namespace <YOUR_PROJECT>\<YOUR_BUNDLE>Bundle\EventListener; use <YOUR_PROJECT>\<YOUR_BUNDLE>Bundle\Entity\User; use FOS\UserBundle\FOSUserEvents; use FOS\UserBundle\Event\UserEvent; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\Security\Core\SecurityContext; use Symfony\Component\Security\Http\SecurityEvents; class LoginListener implements EventSubscriberInterface { /** * @var UrlGeneratorInterface */ private $router; /** * @param UrlGeneratorInterface $router */ public function __construct(UrlGeneratorInterface $router) { $this->router = $router; } /** * {@inheritDoc} */ public static function getSubscribedEvents() { return array( FOSUserEvents::SECURITY_IMPLICIT_LOGIN => 'onImplicitLogin', SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin', ); } /** * @param UserEvent $event */ public function onImplicitLogin(UserEvent $event) { // not implemented yet } /** * @param InteractiveLoginEvent $event */ public function onSecurityInteractiveLogin(InteractiveLoginEvent $event) { $session = new Session(); $user = $event->getAuthenticationToken()->getUser(); if ($user instanceof User) { $session->set('username', $user->getUsername()); } } } 

 <?php // ProfileEditListener namespace <YOUR_PROJECT>\<YOUR_BUNDLE>Bundle\EventListener; use FOS\UserBundle\FOSUserEvents; use FOS\UserBundle\Event\FormEvent; use FOS\UserBundle\Event\UserEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\SecurityContext; class ProfileEditListener implements EventSubscriberInterface { /** * @var UrlGeneratorInterface */ private $router; /** * @param UrlGeneratorInterface $router */ public function __construct(UrlGeneratorInterface $router) { $this->router = $router; } /** * {@inheritDoc} */ public static function getSubscribedEvents() { return array( FOSUserEvents::PROFILE_EDIT_INITIALIZE => 'onProfileInitialize', FOSUserEvents::PROFILE_EDIT_SUCCESS => 'onProfileEdit', FOSUserEvents::PROFILE_EDIT_COMPLETED => 'onProfileCompleted' ); } /** * @param UserEvent $userEvent */ public function onProfileInitialize(UserEvent $userEvent) { $session = new Session(); $session->set('username', $userEvent->getUser()->getUsername()); } /** * @param FormEvent $event */ public function onProfileEdit(FormEvent $event) { // not implemented yet } /** * @param UserEvent $userEvent */ public function onProfileCompleted(UserEvent $userEvent) { $session = new Session(); $session->set('username', $userEvent->getUser()->getUsername()); } } 

Поместите это в свои services.yml вашего Bundle

 services: <YOUR_PROJECT>_user.profile_edit: class: <YOUR_PROJECT>\<YOUR_BUNDLE>Bundle\EventListener\ProfileEditListener arguments: [@router] tags: - { name: kernel.event_subscriber } <YOUR_PROJECT>_user.login_manager: class: <YOUR_PROJECT>\<YOUR_BUNDLE>Bundle\EventListener\LoginListener arguments: [@router] tags: - { name: kernel.event_subscriber } 

И это в шаблоны:

 {{ app.session.get('username') }} 

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

Другая идея заключается в том, что вы удаляете атрибут session в «onProfileCompleted». Затем вы задаете шаблон твига, если установлен атрибут session; если вы не используете имя app.user.username, если true, вы распечатываете app.session.get ('….') … я думаю, что завтра я перейду к этой истории; потому что он больше sf2 сложный (..app.user.username).

Надеюсь, это поможет и сделает вас творческими;)

С уважением