We use the Symfony2 framework and FOSUserBundle for our users. So We have our own UserBundle that inherits from FOSUserBundle. The problem is : When we send the form for editing a user with a wrong password, the app.user.username that is displayed in the header changes when it shouldn't since the form isn't correct. Then when we reload the page, the app.user.username has the normal username (since it hasn't changed in the database). The question is, why does the app.user.username changes temporarily for the value of the form (edit user) that we just sent with a wrong password ?
we use the 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 code that is displayed in the header that should NOT change when submitting the form with a 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 of the formulaire that is displayed :
<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 that we user 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');
}
The FormType inherits from ProfileFormType (Default in FOSUserBundle) thanks to a getParent() function.
Thank you for your help figuring out what is wrong. We don't understand why the app.user.username changes in the header since the form is sent with a wrong password and the data in the database isn't changed and sorry for the comments that are in French in the code :).
This is happening by design. At the moment you call
$form->handleRequest($request)
the user entity is updated with the submitted form data. When you accessapp.user
you are in fact accessing the same user entity. The docs have some more detail about thisYou can use FOSUserBundle-EventListener. (LoginListener|ProfileEditListener) I save the username in the session-context and print them out in twig-template instead of app.user.username.
It works fine.
-
Put this into your services.yml of your Bundle
And this into templates:
I think it is only a work around, i had the same question today.
An other idea is, that you remove the session-attribute in "onProfileCompleted". Then you ask in twig template if session-attribute is set; if not you use the app.user.username, if true you print out app.session.get('....')... i think tomorrow i will change to this story; because it is more sf2 complicated (..app.user.username).
Hope it helps and will make you creative ;)
Regards
This works for me:
1) Add currentUsername property and lifecycle callbacks into User entity class
2) And then in template use:
3) In some cases maybe you should need to override serialize and deserialize methods and add new property (currentUsername) to it.