SonataAdminBundle use Event Subscriber - symfony

I try to set the current authenticated user within a SonataAdminBundle Form:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('title')
->add('content')
->add('slug')
//->add('user')
;
}
Since I don't want the user to select/choose it's own user, I guess I'd like to set the username during the procedure the form was sent by the user (without any user object).
(Note: I'd like to use this in a couple of forms, so I'll need a general solution.)
What I've done now was setting up an EventListener after I've read this: http://symfony.com/doc/current/cookbook/service_container/event_listener.html
class PostListener
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function prePersist(LifeCycleEventArgs $args)
{
$entity = $args->getEntity();
//Get user
$securityContext = $this->container->get('security.context');
$user = $securityContext->getToken()->getUser();
//Set authenticated user as autor
$entity->setUser($user);
}
}
Including service:
post.listener:
class: Backender\BlogBundle\Listener\PostListener
calls:
- [ setContainer, [ #service_container ] ]
tags:
- { name: doctrine.event_listener, event: prePersist }
Not sure now, if this is the right approach, because I wan't to specify the form where I want to set the user. (This one will want to set user on every form right?)
With some more research it seems like I've to use an Event Subscriber like this: http://symfony.com/doc/2.0/cookbook/form/dynamic_form_generation.html
In this example they use FormEvents::PRE_SET_DATA, I guess in my case I have to use POST_SET_DATA.
Here I've some problems!:
1: I'm quiet new to SonataAdminBundle and there we use protected function configureFormFields(FormMapper $formMapper)... where ->addEventSubscriber() isn't available?
2: Is this the right way?, I really didn't find any examples like what I need here for sonata-admin.
I'm thankful for every help!
Best regards...

Try with $formMapper->getFormBuilder()->addEventSubscriber($subscriber);

In the end I found 2 possible solutions for my problem. I'd like to describe these possibilities here because there isn't that much stuff to find about.
Event listener with query of instance of
Call as service:
blog.post.listener:
class: Acme\BlogBundle\Listener\PostListener
arguments: ['#service_container']
tags:
- { name: doctrine.event_listener, event: prePersist }
The listener class:
class PostListener
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function prePersist(LifeCycleEventArgs $args)
{
$entity = $args->getEntity();
//Get user
$securityContext = $this->container->get('security.context');
$user = $securityContext->getToken()->getUser();
if ($entity instanceof Post) {
//Set authenticated user as autor
$entity->setUser($user);
}
}
}
The second way (and maybe the easier) is to use prePersist in Admin Class of SonataAdminBundle
Call as service (use service container or security context as argument):
blog.admin.post:
class: Acme\BlogBundle\Admin\PostAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: Article Handling, label: Posts }
arguments: [null, Acme\BlogBundle\Entity\Post, SonataAdminBundle:CRUD, #service_container]
PostAdmin:
(Note, i use container because i need further stuff in the future)
class PostAdmin extends Admin
{
protected $securityContext;
public function __construct($code, $class, $baseControllerName, ContainerInterface $container)
{
parent::__construct($code, $class, $baseControllerName);
$this->container = $container;
}
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('title')
->add('content')
;
}
public function prePersist($post)
{
$user = $this->container->get('security.context')->getToken()->getUser();
$post->setUser($user);
}
}

I solved adding events to the Admin Class by injecting the Event Dispatcher:
<service id="acme.listener.contract"
class="Acme\AppBundle\Event\ContractListener">
<argument type="service" id="twig"></argument>
<argument type="service" id="mailer"></argument>
<tag name="kernel.event_listener" event="contract.created" method="onContractCreated" />
</service>
Then in my admin class I add the event to an existing method:
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
class ContractAdmin extends Admin
{
protected $dispatcher;
/**
* Security Context
* #var \Symfony\Component\Security\Core\SecurityContextInterface
*/
protected $securityContext;
public function setEventDispatcher(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}
public function setSecurityContext(SecurityContextInterface $securityContext)
{
$this->securityContext = $securityContext;
}
public function prePersist($contract)
{
$user = $this->securityContext->getToken()->getUser();
$contract->setUser($user);
$event = new ContractEvent($contract);
$dispatcher = $this->dispatcher;
$dispatcher->dispatch(
ContractEvents::CONTRACT_CREATED,
$event
);
}
This allows me to add extra events I am sharing across my app at other places too.
In your case you can simply inject the Security Context only:
sonata.admin.contract:
class: Acme\AppBundle\Admin\ContractAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: "Contracts", label_catalogue: "messages", label: "Auftragsübersicht" }
arguments:
- ~
- Acme\AppBundle\Entity\Contract
- ~
calls:
- [ setSecurityContext, [#security.context]]
- [ setEventDispatcher, [#event_dispatcher]]

Related

Symfony 3.4: Access doctrine within an EventListener

I am trying to access to doctrine within an EventListener which tests a database table, if it is invalid then the user will be redirected to a config page where he will fix the issues!
The test will be executed before every call to a controller, so I will use the Event kernel.controller:
[EDITED: SOLVED, TESTING AND REDIRECTING VERY FINE]
<?php
namespace AdminBundle\EventListener;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Doctrine\ORM\EntityManager;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
class BuildReferencesEventListener
{
/**
* #var EntityManager
*/
protected $em;
/**
* #var Router
*/
protected $router;
protected $references = [
1 => 'oxygen',
2 => 'pH',
3 => 'pCO2'
];
protected $redirect_route = 'references';
protected $requestStack;
protected $session;
public function __construct(EntityManager $entityManager, Router $router, RequestStack $requestStack, Session $session)
{
$this->em = $entityManager;
$this->router = $router;
$this->requestStack = $requestStack;
$this->session = $session;
}
public function onKernelController()
{
$em = $this->em;
$savedReferences = $em->getRepository('AdminBundle:ParamReference')->findAll();
$references = $this->references;
if (count($savedReferences) <= 0){
$this->redirect();
}
for ($i =0; $i<count($savedReferences); $i++){
if ($savedReferences[$i] !== $references[$i]) {
$this->redirect();
}
}
}
public function redirect()
{
$request = $this->requestStack->getCurrentRequest();
$route = $request->get('_route');
$this->session->getFlashBag()->add('warning', 'You need to setup the references for the parameters before starting the work');
if ($route != 'references'){
$url = $this->router->generate('references');
$redirect = new RedirectResponse($url);
$redirect->send();
}
}
}
Here is services.yml
build.references:
class: AdminBundle\EventListener\BuildReferencesEventListener
arguments: ['#doctrine.orm.entity_manager', '#router', '#request_stack', '#session']
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
Add argument inside your service.yml event listner service section
Your.service:
Class: AdminBundle\EventListener\BuildReferencesEventListener
arguments: ['#doctrine.orm.entity_manager']
tags:
- { name: kernel.event_listener, event: kernel.controller }
And add this __cunstruct code inside your listner:
namespace AdminBundle\EventListener;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Doctrine\ORM\EntityManager;
class BuildReferencesEventListener
{
protected $em;
function __construct(EntityManager $em)
{
$this->em = $em;
}
public function onKernelController(FilterControllerEvent $event)
{
//TO DO call doctrine
// $em = Enity Manager...
// $em->getRepository...
// I hope its clear enough what im trying to do
}
In Symfony 3.4 and above, autowiring is enabled by default, so you just have to add a type-hinted argument to your service's constructor method and a private property to assign the argument's value to.
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class BuildReferencesEventListener
{
private $em;
public __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function onKernelController(FilterControllerEvent $event)
{
$em = this->em;
// [...]
}
}
If you need to
declare arguments explicitly, you could do this in your service configuration:
# config/services.yaml
services:
# ... same code as before
# explicitly configure the service
AdminBundle\EventListener\BuildReferencesEventListener:
arguments:
$em: '#doctrine.orm.entity_manager_interface'
This could be useful to pass parameters that the service container doesn't know.
To learn more about the service container, https://symfony.com/doc/3.4/service_container.html#injecting-services-config-into-a-service.

Symfony monolog after doctrine persist

I need to log all my users actions with monolog. But only if the actions persist data with doctrine, insert, update or delete.
What should I do ? Could I define a generic method like "afterPersist" to log every action ?
Thx !
EDIT :
The Listener :
use Doctrine\ODM\MongoDB\Event\OnFlushEventArgs;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\SecurityContextInterface;
class DatabaseLogger
{
protected $logger;
protected $security_context;
protected $request;
public function __construct(LoggerInterface $logger, ContainerInterface $service_container)
{
$this->logger = $logger;
$this->setSecurityContext($service_container->get('security.context'));
}
public function setRequest(RequestStack $request_stack)
{
$this->request = $request_stack->getCurrentRequest();
}
public function setSecurityContext(SecurityContextInterface $security_context)
{
$this->security_context = $security_context;
}
public function onFlush(OnFlushEventArgs $args)
{
// configure this however you want
}
}
and in service.yml
cc.listener.database_logger:
class: Cc\HitoBundle\Listener\DatabaseLogger
tags:
- { name: doctrine_mongodb.odm.event_listener, event: onFlush }
- { name: monolog.logger, channel: database_access }
calls:
- [ setRequest, [#request_stack] ]
arguments: [ #logger, #service_container ]
I got an error when I add the security context :
ServiceCircularReferenceException: Circular reference detected for service "doctrine_mongodb.odm.default_document_manager", path: "doctrine_mongodb.odm.default_document_manager -> doctrine_mongodb.odm.default_connection -> doctrine_mongodb.odm.event_manager -> cc.listener.post_persist -> security.context -> security.authentication.manager -> security.user.provider.concrete.user_db".
Register a listener with something like:
Build a listener:
namespace Acme\MyBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
class PersistLogger
{
public $logger;
public function __construct($logger)
{
$this->logger = $logger;
}
public function postPersist(LifecycleEventArgs $args)
{
// configure this however you want
$this->logger->addDebug('whatever');
}
}
Register the listener in config.yml
acme_mybundle.eventlistener.persistlogger:
class: Acme\MyBundle\EventListener\PersistLogger
tags:
- { name: doctrine.event_listener, event: postPersist }
argument: [ #logger ]
EDIT:
Injecting the security context into a doctrine listener causes a circular reference exception if you are storing your users in the database (e.g. with FOSUserBundle). This is because the security context needs to inject the entity manager so it can get users from the database, but because of the listener, the entity manager depends on the security context.
The workaround is to inject the whole service container (one of the only times doing this is justified), and get the security context from there:
namespace Acme\MyBundle\EventListener;
use Psr\Log\LoggerInterface,
Symfony\Component\DependencyInjection\ContainerInterface,
Symfony\Component\Security\Core\SecurityContextInterface;
protected $service_container;
protected $logger;
public function __construct(LoggerInterface $logger, ContainerInterface $service_container)
{
$this->service_container = $service_container;
$this->logger = $logger;
}
public function getSecurityContext()
{
return $this->service_container->get('security.context');
}
and
acme_mybundle.eventlistener.persistlogger:
class: Acme\MyBundle\EventListener\PersistLogger
tags:
- { name: doctrine.event_listener, event: postPersist }
argument: [ #logger, #service_container ]
I think that you may have a look to the cookbook, there is a very nice entry that talk about Doctrine's events.
In addition, you may have a look to the method to create custom monolog chanels.

How to Redirect out of an Event?

I am listening to security.authentication.success event. Everytime a customer logs in i need to check if he still got main information. If not, then i want to redirect him to the form for creation. It is running good but in which way can i redirect out of an event? Simply returning an Redirect Object does not work.
Service:
applypie_userbundle.newuser_listener:
class: Applypie\Bundle\UserBundle\EventListener\NewUserListener
arguments: [#router]
tags:
- { name: kernel.event_listener, event: security.authentication.success, method: onLogin }
Listener:
namespace Applypie\Bundle\UserBundle\EventListener;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Router;
class NewUserListener
{
protected $context;
protected $router;
public function __construct(Router $router)
{
$this->router = $router;
}
/**
* #param AuthenticationEvent $event
*
* if current user neither have an applicant row or an company row, redirect him to create applicant row
*/
public function onLogin(AuthenticationEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if(!$user->getApplicant() && !count($user->getCompanies()))
{
return new RedirectResponse($this->router->generate('applypie_user_applicant_create'));
}
}
}
To cut the long story short, yes, you are listening to the correct event but... is someone waiting your method return?... NOPE!
I can't test this right now, but this is the idea:
security.yml
firewalls:
main:
pattern: ^/
form_login:
success_handler: my.security.login_handler
services.yml
services:
my.security.login_handler:
class: Applypie\Bundle\UserBundle\EventListener\NewUserListener
arguments: [#router]
NewUserListener
namespace Applypie\Bundle\UserBundle\EventListener;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Request;
class NewUserListener implements AuthenticationSuccessHandlerInterface
{
protected $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
protected function getResponse($name)
{
$url = $this->router->generate($name);
$response = new RedirectResponse($url);
return $response;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
$route = 'user_home';
if(!$user->getApplicant() && empty($user->getCompanies())) {
$route = 'applypie_user_applicant_create';
}
return $this->getResponse($route);
}
}
Sources:
http://api.symfony.com/2.3/Symfony/Component/Security/Http/Authentication/AuthenticationSuccessHandlerInterface.html
Do something just after symfony2 login success and before redirect?

Symfony2: check-email after registration

First time I'm posting a message in this forum, which I use regularly. I use FOSUserbundle in my Symfony2 application to manage users. I activated the sending of the email confirmation when users create an account by the following thing:
fos_user:
registration:
confirmation:
enabled: true
It works very well: the email is sent successfully. I am redirected to the page /check-email that says that this email has been sent. However, I would like to change the redirection : I would like to be redirected to my index page and not to /check-email. So I did my research and I knew he had to go through the FOSUserBundle events (list here).
What I did :
class RegistrationListener implements EventSubscriberInterface {
private $router;
public function __construct(UrlGeneratorInterface $router) {
$this->router = $router;
}
public static function getSubscribedEvents() {
return array(
FOSUserEvents::REGISTRATION_CONFIRM => 'onRegistrationConfirm');
}
public function onRegistrationConfirm(FormEvent $event) {
$url = $this->router->generate('listeArticlesAccueil');
$event->setResponse(new RedirectResponse($url));
}
}
and services.yml
services:
listener_user.registration_listener:
class: My\Soft\UserBundle\EventListener\RegistrationListener
arguments: [#router]
tags:
- { name: kernel.event_subscriber }
The problem is that every time I am redirected to the page /check-email. I therefore told me that it was probably the wrong event. So I also tried REGISTRATION_SUCCESS. But nothing changes. So either I have not used the right event, or I'm doing something wrong.
In either case, I hope you can help me !
Thanks a lot and sorry for my bad english ;)
I know that this question has been posted a long time ago, but i found a solution and I would like to share it to others that have the same issue.
In this case, the event that you should use is REGISTRATION_SUCCESS.
You have to prioritize the listener REGISTRATION_SUCCESS to be called before FOS\UserBundle\EventListener\EmailConfirmationListener::onRegistrationSuccess like this:
public static function getSubscribedEvents()
{
return [
FOSUserEvents::REGISTRATION_SUCCESS => [
['onRegistrationSuccess', -10],
],
];
}
If you don't do it, EmailConfirmationListener will be called earlier and you will be redirected to fos_user_registration_check_email route.
Here is what I did:
class RedirectAfterRegistrationSubscriber implements EventSubscriberInterface
{
private $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function onRegistrationSuccess(FormEvent $event)
{
$event->stopPropagation();
$url = $this->router->generate('homepage');
$response = new RedirectResponse($url);
$event->setResponse($response);
}
public static function getSubscribedEvents()
{
return [
FOSUserEvents::REGISTRATION_SUCCESS => ['onRegistrationSuccess',-10],
];
}
}
And in app/services.yml:
app.redirect_after_registration_subscriber:
class: AppBundle\EventListener\RedirectAfterRegistrationSubscriber
arguments: ['#router']
tags:
- { name: kernel.event_subscriber }
I hope this helps.
I would bet on REGISTRATION_SUCCESS.
The doc (https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/FOSUserEvents.php) says
that this event allows to set the response.
REGISTRATION_CONFIRM only allows to access the user
Instead of that use REGISTRATION_CONFIRM.
Here is the complete code for the event listener:
/**
* Description of RegisterationConfirmListener
*
* #author imran
*/
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Description of RegisterationConfirmListener
*
* #author imran
*/
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Listener responsible to change the redirection at the end of the password resetting
*/
class RegisterationConfirmListener implements EventSubscriberInterface {
private $router;
public function __construct(UrlGeneratorInterface $router) {
$this->router = $router;
}
public static function getSubscribedEvents() {
return [
FOSUserEvents::REGISTRATION_CONFIRM => 'onRegisterationConfirm',
];
}
public function onRegisterationConfirm(GetResponseUserEvent $event) {
$url = $this->router->generate('home');
$event->setResponse(new RedirectResponse($url));
}
}
For Services.yml:
app_user.registeration_confirmed:
class: Wishlocker\AppBundle\EventListener\RegisterationConfirmListener
arguments: [ #router ]
tags:
- { name: kernel.event_subscriber }

Symfony2 wrong locale detection?

Following Symfony2 guide about translation i found that inferred locale from http headers (stored in $this->get('session')->getLocale()) is wrong (sent it, inferred en):
Host localhost User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64;
rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Accept text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language it-it,it;q=0.8,en-us;q=0.5,en;q=0.3
Is this a normal behaviour? Or should i set something in order to get localization working out of the box?
I looked more thoroughly onto the code today, because I was experiencing the same problem as you, and it appears that the language comes from Session::getLocale(). But Symfony2 never calls Session::setLocale(), and sets the locale member of the Session object. A google search for "symfony2 session setlocale" leads to this § of the documentation
So I ended up putting this line on top of the controller I was working on, and it worked :
$this->getRequest()->getSession()->setLocale(
$this->getRequest()->getPreferredLanguage());
Now I guess this is not acceptable, because you're not going to add this on top of each and every controller. Plus, this should not be done for every request, it should only be done for the first one, when the user has no session yet. If anyone knows how to do this feel free to edit this answer.
per HTTP-Standard you should be using a different URL for each translated version of the page. What remains is a simple action that will infer the best-to-use locale from the request and redirect to the corresponding page:
/**
* #Route("/")
*/
public function localeRedirectAction() {
/* #var $request \Symfony\Component\HttpFoundation\Request */
/* #var $session \Symfony\Component\HttpFoundation\Session */
$req = $this->getRequest();
$session = $this->get('session');
$session->setLocale($req->getPreferredLanguage(array('de', 'en')));
return $this->redirect($this->generateUrl('home'));
}
if you need to do this for any page, you'll basically need to do the same, but within a Listener for the kernel.request-Event. In order to be reliably called after the route-matcher did it's work you should set the priority of the listener to a value < 0:
# services.yml
services:
my_locale_listener:
class: Namespace\LocaleListener
tags: [{ name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: -255 }]
arguments: [ #service_container, [ 'en', 'fr', 'de', 'es', 'it' ] ]
the listener would then look like this:
class LocaleListener {
public function __construct($container, $availableLocales) {
$this->container = $container;
$this->availableLocales = $availableLocales;
}
public function onKernelRequest(GetResponseEvent $e) {
$req = $e->getRequest();
$locale = $req->getPreferredLanguage($this->availableLocales);
// generate a new URL from the current route-name and -params, overwriting
// the current locale
$routeName = $req->attributes->get('_route');
$routeParams = array_merge($req->attributes->all(), array('_locale' => $locale));
$localizedUrl = $this->container->get('router')->generateUrl($routeName, $routeParams);
$e->setResponse(new RedirectResponse($localizedUrl));
}
}
P.S. i'm not entirely confident that this code actually works, but it should give a basic idea on how this could be done.
You can register listener like follow:
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class LocaleListener
{
private $container;
private $defaultLocale;
public function __construct(ContainerInterface $container, $defaultLocale = 'nl')
{
$this->container = $container;
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
if (!$this->container->has('session')) {
return;
}
$session = $this->container->get('session');
$session->setLocale($this->defaultLocale);
}
}
(gist)
Just after framework session setup stage:
<service id="my.event_listener.locale_listener" class="MyBundle\MyEventListener\LocaleListener">
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="100" />
<argument type="service" id="service_container" />
</service>
(gist)
I wrote a LocaleListener which is not redirecting you to a locale-specific url, it does however set the locale settings for you ;)
Code in the services.yml
services:
acme.demobundle.listener.request:
class: Namespace\LocaleListener
tags: [{ name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: -255 }]
arguments: [ #service_container, [ 'en', 'fr', 'de', 'it', 'es' ] ]
and the actual listener:
class LocaleListener
{
protected $container;
protected $availableLocales;
public function __construct(\Symfony\Component\DependencyInjection\Container $container, $availableLocales) {
$this->container = $container;
$this->availableLocales = $availableLocales;
}
public function onKernelRequest(GetResponseEvent $e) {
$req = $e->getRequest();
$locale = $req->getPreferredLanguage($this->availableLocales);
$session = $this->container->get('session');
$session->setLocale($locale);
}
}

Resources