编辑用户错误地暂时更改了app.user.用户名,如何解决


Editing User with error wrongly changes the app.user.username temporarily, how to solve it?

我们使用Symfony2框架和FOSUserBundle供用户使用。所以我们有自己的 UserBundle,它继承自 FOSUserBundle。问题是:当我们发送表单以使用错误的密码编辑用户时,标题中显示的 app.user.username 会在不应该更改时更改,因为表单不正确。然后,当我们重新加载页面时,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>

这是我们为此特定表单使用的表单类型:

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 会在标题中更改,因为表单是使用错误的密码发送的,并且数据库中的数据没有更改,对于代码:)中的法语注释表示抱歉。

这对

我有用:

1) 将当前用户名属性和生命周期回调添加到用户实体类中

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

这是设计使然。在您调用$form->handleRequest($request)时,用户实体将使用提交的表单数据进行更新。当您访问app.user时,您实际上是在访问同一个用户实体。文档对此有一些更多详细信息

您可以使用

FOSUserBundle-EventListener。(登录监听|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:
    <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') }}

我认为这只是一个解决方法,我今天有同样的问题。

另一个想法是,您删除"onProfileComplete"中的会话属性。然后您在树枝模板中询问是否设置了会话属性;如果不是,则使用 app.user.username,如果为 true,则打印出 app.session.get('....')...我想明天我会改成这个故事;因为它更复杂 SF2 (.app.user.username)。

希望它有帮助,并会让你有创意;)

问候