Editing a user with an error incorrectly changes the username app.user.username, how to solve it?

We use the Symfony2 platform and the FOSUserBundle for our users. So, we have our own UserBundle, which inherits from FOSUserBundle. The problem is this: when we submit the form for editing the user with the wrong password, the name app.user.us that appears in the header changes when it should not, because the form is incorrect. Then, when we reload the page, app.user.username has the usual username (since it has not changed in the database). The question is why app.user.username is temporarily changing for the form value (edit user) that we just submitted with the wrong password?

we use normal routing from 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

And the default controller from 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()
    ));
}

Here is the part of the code that appears in the header, which should NOT change when submitting the form with the wrong password. (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>

And here is the code for the displayed formula:

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

And here is the FormType form that we use for this particular form:

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 inherits from ProfileFormType (by default in FOSUserBundle) thanks to the getParent () function.

Thank you for your help in finding out what is wrong. We do not understand why the name app.user.username changes in the header, because the form is submitted with the wrong password, and the data in the database does not change and sorry for the comments that are in French in the code :).

+4
3

. $form->handleRequest($request), . app.user, .

0

:

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

+2

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

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

, ;)

0

All Articles