I create my app with Symfony 2.8 and FosUserBundle. I wiew a lot of tutorials and I don´t understand why it does not take the value of the user session.
I do it like the example in the web of symfony: https://symfony.com/doc/2.8/session/locale_sticky_session.html
The configuration of the first Event Listener is correct but the second event Listener doesn't set the local value in session
<?php
//AppBundle/Controller/DefaultController.php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
/**
* #Route("/", name="homepage")
*/
public function indexAction(Request $request)
{
// replace this example code with whatever you need
$locale = $request->getLocale();
$user = $event->getAuthenticationToken()->getUser();
var_dump($locale);
return $this->render('default/index.html.twig', array(
'base_dir' => realpath($this->container->getParameter('kernel.root_dir') . '/..'),
));
/*return $this->render('default/index.html.twig', array(
'base_dir' => realpath($this->container->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
));*/
}
}
Event Listeners:
<?php
// src/AppBundle/EventListener/LocaleListener.php
namespace AppBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class LocaleListener implements EventSubscriberInterface
{
private $defaultLocale;
public function __construct($defaultLocale = 'en')
{
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
// try to see if the locale has been set as a _locale routing parameter
if ($locale = $request->attributes->get('_locale')) {
$request->getSession()->set('_locale', $locale);
} else {
// if no explicit locale has been set on this request, use one from the session
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
}
public static function getSubscribedEvents()
{
return array(
// must be registered after the default Locale listener
KernelEvents::REQUEST => array(array('onKernelRequest', 15)),
);
}
}
UserLocaleListener.php
<?php
// src/AppBundle/EventListener/UserLocaleListener.php
namespace AppBundle\EventListener;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
/**
* Stores the locale of the user in the session after the
* login. This can be used by the LocaleListener afterwards.
*/
class UserLocaleListener
{
/**
* #var Session
*/
private $session;
public function __construct(Session $session)
{
$this->session = $session;
}
/**
* #param InteractiveLoginEvent $event
*/
public function onInteractiveLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if (null !== $user->getLocale()) {
$this->session->set('_locale', $user->getLocale());
}
}
}
The configuration of config.yml is:
parameters:
locale: en
fos_user:
db_driver: orm
firewall_name: main
user_class: AppBundle\Entity\User
from_email:
address: "%mailer_user%"
sender_name: "%mailer_user%"
registration:
confirmation:
enabled: true
resetting:
email:
from_email:
address: manuelterronalvarez#gmail.com
sender_name: App Resetting
jms_i18n_routing:
default_locale: "%locale%"
locales: [es, en]
strategy: prefix_except_default
Any ideas? Thanks in advance
Related
I'm trying to change the user locale with Symfony from a field "locale" in the database. I read the Symfony manual (how to sticky a session for example), but nothing works in my application. Translator still gets the default locale...
I created listeners, subscribers... to dynamically change the locale, but as they are loaded before the firewall listener, I'm unable to change the current value.
I tried to change the priority subscriber, but I lost the user entity. I tried to set locale request in controllers, but I think it's too late.
I don't want to add locales in URLs.
Here my subscriber - listener - code:
public function onKernelRequest(RequestEvent $event)
{
$user = $this->tokenStorage->getToken()->getUser();
$request = $event->getRequest();
$request->setLocale($user->getLocale());
}
In subscribers, I added:
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => [['onKernelRequest', 0]],
];
}
Here, my full code:
framework.yml:
default_locale: fr
services.yml:
parameters:
locale: 'fr'
app_locales: fr|en|
translation.yml:
framework:
default_locale: '%locale%'
translator:
paths:
- '%kernel.project_dir%/translations'
fallbacks:
- '%locale%'
LocaleSubscriber.php:
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class LocaleSubscriber implements EventSubscriberInterface
{
private $defaultLocale;
public function __construct($defaultLocale = 'en')
{
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
// try to see if the locale has been set as a _locale routing parameter
if ($locale = $request->attributes->get('_locale')) {
$request->getSession()->set('_locale', $locale);
} else {
// if no explicit locale has been set on this request, use one from the session
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
}
public static function getSubscribedEvents()
{
return [
// must be registered before (i.e. with a higher priority than) the default Locale listener
KernelEvents::REQUEST => [['onKernelRequest', 20]],
];
}
}
UserLocaleSubscriber.php
// src/EventSubscriber/UserLocaleSubscriber.php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
/**
* Stores the locale of the user in the session after the
* login. This can be used by the LocaleSubscriber afterwards.
*/
class UserLocaleSubscriber implements EventSubscriberInterface
{
private $session;
public function __construct(SessionInterface $session)
{
$this->session = $session;
}
public function onInteractiveLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if (null !== $user->getLocale()) {
$this->session->set('_locale', $user->getLocale());
}
}
public static function getSubscribedEvents()
{
return [
SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin',
];
}
}
Ex controller annotation:
/**
* #Route("/user/locale", name="user_locale", requirements={"_locale" = "%app.locales%"})
* #Route("/{_locale}/user/locale", name="user_locale_locale", requirements={"_locale" = "%app.locales%"})
*/
Find the priority of the firewall listener using debug:event kernel.request.
Make sure your UserLocaleSubscriber is executed right after the firewall listener.
Autowire the TranslatorInterface and manually set the translator locale.
// src/EventSubscriber/UserLocaleSubscriber.php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Stores the locale of the user in the session after the
* login. This can be used by the LocaleSubscriber afterwards.
*/
class UserLocaleSubscriber implements EventSubscriberInterface
{
private $session;
private $translator;
public function __construct(SessionInterface $session, TranslatorInterface $translator)
{
$this->session = $session;
$this->translator = $translator;
}
public function onInteractiveLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if (null !== $user->getLocale()) {
$this->translator->setLocale($user->getLocale());
}
}
public static function getSubscribedEvents()
{
return [
SecurityEvents::INTERACTIVE_LOGIN => ['onInteractiveLogin', 7]
];
}
}
It's hard to help without seeing the full code you are using.
Symfony has it's own LocaleListener as well. Make sure your's is executed first.
/**
* #return array
*/
public static function getSubscribedEvents()
{
return [
// must be registered before (i.e. with a higher priority than) the default Locale listener
KernelEvents::REQUEST => [['onKernelRequest', 20]],
];
}
I am New to Symfony 3 and I have an Issue when I try to activate my account.
I extend my User Entity from FOSUserBundle. I have activated the confirmation system of FOS_User.
Furthermore, when a user register into my app, he must upload a file. To do this, I created a FileUploader service and a ImageUploadListener listener.
The problem is when I click on my activation link from my gmail's email, I get the following error:
Uncaught PHP Exception Symfony\Component\Debug\Exception\ContextErrorException: "Notice: Undefined variable: fileName" at /home/clement/Rendu/tech-web/src/UserBundle/EventListener/ImageUploadListener.php line 49
Could you help me ?
Thank you in advance from a french dev with a big headache !
PS: I followed this tutorial to implement my file upload functionality.
User.php
namespace UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*
* #Assert\NotBlank(message="Please enter your phone.", groups={"Registration", "Profile"})
* #Assert\Length(
* min=3,
* max=255,
* minMessage="The phone is too short.",
* maxMessage="The phone is too long.",
* groups={"Registration", "Profile"}
* )
*/
protected $phone;
/**
* #ORM\Column(type="string")
*
* #Assert\NotBlank(message="Please, upload a file.")
* #Assert\File(mimeTypes={ "application/pdf", "image/jpeg" })
*/
private $image;
public function __construct()
{
parent::__construct();
// your own logic
$this->roles = array('ROLE_USER');
}
public function getPhone() {
return $this->phone;
}
public function setPhone($phone) {
$this->phone = $phone;
}
public function setImage($image)
{
$this->image = $image;
return $this;
}
public function getImage()
{
return $this->image;
}
}
RegistrationType.php
namespace UserBundle\Form;
use UserBundle\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('phone')
->add('image', FileType::class);
}
public function getParent()
{
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
// Or for Symfony < 2.8
// return 'fos_user_registration';
}
public function getBlockPrefix()
{
return 'app_user_registration';
}
// For Symfony 2.x
public function getName()
{
return $this->getBlockPrefix();
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => User::class,
));
}
}
FileUploader.php
namespace UserBundle\Service;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class FileUploader
{
private $targetDir;
public function __construct($targetDir)
{
$this->targetDir = $targetDir;
}
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($this->getTargetDir(), $fileName);
return $fileName;
}
public function getTargetDir()
{
return $this->targetDir;
}
}
ImageUploadListener
namespace UserBundle\EventListener;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use UserBundle\Entity\User;
use UserBundle\Service\FileUploader;
use Symfony\Component\HttpFoundation\File\File;
class ImageUploadListener
{
private $uploader;
private $fileName;
public function __construct(FileUploader $uploader)
{
$this->uploader = $uploader;
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
public function preUpdate(PreUpdateEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
private function uploadFile($entity)
{
// upload only works for User entities
if (!$entity instanceof User) {
return;
}
$file = $entity->getImage();
// only upload new files
if ($file instanceof UploadedFile) {
$fileName = $this->uploader->upload($file);
}
$entity->setImage($fileName);
}
}
services.yml
parameters:
#parameter_name: value
services:
_defaults:
autowire: true
autoconfigure: true
public: false
AppBundle\:
resource: '../../src/AppBundle/*'
exclude: '../../src/AppBundle/{Entity,Repository,Tests}'
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
app.form.registration:
class: UserBundle\Form\RegistrationType
tags:
- { name: form.type, alias: app_user_registration }
UserBundle\Service\FileUploader:
arguments:
$targetDir: '%images_directory%'
UserBundle\EventListener\ImageUploadListener:
tags:
- { name: doctrine.event_listener, event: prePersist }
- { name: doctrine.event_listener, event: preUpdate }
config.yml
fos_user:
db_driver: orm
firewall_name: main
user_class: UserBundle\Entity\User
service:
mailer: fos_user.mailer.twig_swift
from_email:
address: "%mailer_user%"
sender_name: "%mailer_user%"
registration:
form:
type: UserBundle\Form\RegistrationType
confirmation:
enabled: true
template: '#FOSUser/Registration/email.txt.twig'
from_email:
address: "%mailer_user%"
sender_name: "%mailer_user%"
profile:
form:
type: UserBundle\Form\EditType
Your event is launched each time you save an user. When you activate the user account you update the user with doctrine in the database so the preUpdate event is call.
To avoid the notice you should do this :
// only upload new files
if ($file instanceof UploadedFile) {
$fileName = $this->uploader->upload($file);
$entity->setImage($fileName);
}
And to avoid to call each time your uploader you have to check if the file is already upload or override the register action to upload it instead of use a doctrine event to do this
Description:
I would like to use a Listener on my LoginAction but the one I am using, isn't working.
I have an Entity :
class User implements AdvancedUserInterface {
public function isEnabled(){}
...
}
}
So, when, I try to connect my user i have the good message :
user is disabled
On this action, I use an ExceptionListener like this :
# src/AppBundle/EventListener/ExceptionListener.php
namespace AppBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Exception\DisabledException;
class ExceptionListener
{
private $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
die('-------');
$ex = $event->getException();
if($ex instanceof DisabledException) {
$url = $this->router->generate('accounts.please_activate');
$response = new RedirectResponse($url);
$event->setResponse($response);
}
}
}
My listener work, cause, if i do a die in the construct, it's ok, but, my die doesn't work in the function onKernelException
So my redirect to the specific route doesn't work...
services.yml
services:
app.exception_listener:
class: AppBundle\EventListener\ExceptionListener
arguments: ['#router']
tags:
- { name: kernel.event_listener, event: kernel.exception }
Question:
Why am I unable to redirect a user that is considered "disabled" to a specific route?
On a second note, is this considered good practice?
You should handle the login failures with customized login handlers.
Creare a new service that implements Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface:
namespace AppBundle\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\DisabledException;
class FailLoginHandler implements AuthenticationFailureHandlerInterface
{
private $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
if($exception instanceof DisabledException) {
$url = $this->router->generate('accounts.please_activate');
return new RedirectResponse($url);
}
}
}
Define a new service
services:
app.login_fail:
class: AppBundle\Security\FailLoginHandler
arguments: ['#router']
then add the failure_handler option under the configuration of your firewall on security.yml:
security:
firewalls:
firewallname:
failure_handler: app.login_fail
I use Symfony 2.6.0
I need to set the language of the user instead of the default language.
Services to change locale:
helper.interactive_login_listener:
class: HelperBundle\Listeners\LanguageListener
calls:
- [ setSession, [#session] ]
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: setLocaleForAuthenticatedUser }
helper.language.kernel_request_listener:
class: HelperBundle\Listeners\LanguageListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: setLocaleForUnauthenticatedUser }
Code:
namespace HelperBundle\Listeners;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class LanguageListener
{
private $session;
public function setSession(Session $session)
{
$this->session = $session;
}
/**
* kernel.request event. If a guest user doesn't have an opened session, locale is equal to
* "undefined" as configured by default in parameters.ini. If so, set as a locale the user's
* preferred language.
*
* #param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
*/
public function setLocaleForUnauthenticatedUser(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
$request = $event->getRequest();
$request->setLocale('ru');
}
/**
* security.interactive_login event. If a user chose a language in preferences, it would be set,
* if not, a locale that was set by setLocaleForUnauthenticatedUser remains.
*
* #param \Symfony\Component\Security\Http\Event\InteractiveLoginEvent $event
*/
public function setLocaleForAuthenticatedUser(InteractiveLoginEvent $event)
{
$this->session->set('_locale', 'ru');
}
}
Config:
framework:
translator: ~
default_locale: ~
In Controller:
namespace HelperBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
abstract class PrototypeController extends Controller
{
protected $model;
protected $flashBag;
//Метод загрузки модели контроллера
abstract function loadServiceModelEvent($model_service_name);
//Вывод шаблона
protected function renderPage($template, $response)
{
print $this->get('request')->getLocale().'==';
print $this->get('request')->getSession()->get('_locale'); die();
Result print:
ru==ru
At the same time, English (en) is used.
But, if set
framework:
translator: ~
default_locale: ru
Then everything is fine.
How to change language?
Thank you!
If I understand the question correctly, the problem is that the language is not set properly by your listener. Problem with listeners in this case is, that you cannot set priority for their execution. That is when EventSubscriber comes to the party.
# app/config/services.yml
services:
app.exception_subscriber:
class: AppBundle\EventSubscriber\LanguageListener
tags:
- { name: kernel.event_subscriber }
Your listener in that case will sth like this:
class LanguageListener implements EventSubscriberInterface
{
private $session;
public function setSession(Session $session)
{
$this->session = $session;
}
/**
* #return array
*/
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => [
['onKernelRequest', 9],
],
];
}
public function onKernelRequest(GetResponseEvent $event)
{
...
}
}
As you can see the difference is not big, BUT in the getSubscribedEvents function you can specify the priority which will be used (in this case 9). You can read more about the priorities in the official documentation.
You need to make sure, that the subscriber will be executed before the initialization of LocaleListener from the Symfony framework. That listener (actually subscriber) "messes up" with the settings and sets the defaults if othing is there. So if you intercept the locale before, it should work pretty fine.
You can check all the registered listeners and subscribers via CLI command:
php bin/console debug:event-dispatcher
I'm following the FOSUserbundle Official documentation in order to change the redirection after a password resetting procedure. I created a new class named PasswordResettingListener and add it to services.yml
The class is executes up to the getSubscribedEvents() method but Symfony never calls my specific method that performs the redirect. Am I misunderstanding something?
src/AppBundle/EventListener/PasswordResettingListener.php:
<?php
// src/AppBundle/EventListener/PasswordResettingListener.php
namespace AppBundle\EventListener;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class PasswordResettingListener implements EventSubscriberInterface {
private $router;
public function __construct(UrlGeneratorInterface $router){
$this->router = $router;
}
/**
* CALLED!
*/
public static function getSubscribedEvents() {
return array(
FOSUserEvents::RESETTING_RESET_SUCCESS => 'onPasswordResettingSuccess',
);
}
public function onPasswordResettingSuccess(FormEvent $event) {
die("alone"); //never called
$url = $this->router->generate('tarifs'); //nevercalled
die("die alone !!!!!"); // never called
$event->setResponse(new RedirectResponse($url)); // ""
}
}
The service is recorded here (/app/config/services.yml):
# app/config/services.yml
services:
app.form.registration:
class: AppBundle\Form\RegistrationType
tags:
- { name: form.type, alias: app_user_registration }
app.password_resetting:
class: AppBundle\EventListener\PasswordResettingListener
arguments: [#router]
tags:
- { name: kernel.event_subscriber }
src/AppBundle/EventListener/PasswordResettingListener.php:
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class PasswordResettingListener implements EventSubscriberInterface
{
private $router;
private $security;
public function __construct(UrlGeneratorInterface $router, AuthorizationChecker $security) {
$this->router = $router;
$this->security = $security;
}
public static function getSubscribedEvents() {
return [
FOSUserEvents::RESETTING_RESET_SUCCESS => 'onPasswordResettingSuccess',
];
}
public function onPasswordResettingSuccess(FormEvent $event) {
$url = $this->router->generate('homepage');
$event->setResponse(new RedirectResponse($url));
}
}
and app/config/services.yml services:
acme_user.password_resetting:
class: src/AppBundle/EventListener/PasswordResettingListener
arguments: [ "#router", "#security.authorization_checker" ]
tags:
- { name: kernel.event_subscriber }
You can use FOSUser's routing in order to change the redirect.
When importing it's routes, you have two options: either you add all by adding
fos_user:
resource: "#FOSUserBundle/Resources/config/routing/all.xml"
or each of them, by path. This is the one for the user's profile:
fos_user_profile:
resource: "#FOSUserBundle/Resources/config/routing/profile.xml"
prefix: /profile
Just change it's prefix by your desired path and that's all.
PS: I tried to use Event Listener as well but it didn't worked.
Good luck!