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

Мы используем платформу Symfony2 и FOSUserBundle для наших пользователей. Итак, у нас есть собственный UserBundle, который наследуется от FOSUserBundle.
Проблема заключается в следующем: когда мы отправляем форму для редактирования пользователя с неверным паролем, имя app.user.user, отображаемое в заголовке, изменяется, когда не должно, поскольку форма неверна. Затем, когда мы перезагружаем страницу, 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 изменяется в заголовке, поскольку форма отправляется с неверным паролем, а данные в базе данных не изменяются, и извините за комментарии, которые написаны на французском языке в коде :).

4

Решение

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

0

Другие решения

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

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

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).

2

Вы можете использовать FOSUserBundle-EventListener. (LoginListener | ProfileEditListener) Я сохраняю имя пользователя в контексте сеанса и распечатываю его в шаблоне twig вместо 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') }}

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

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

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

С уважением

0
По вопросам рекламы [email protected]