Subscribtion to FOSUserEvents::REGISTRATION_COMPLETED is running twice - symfony

In symfony 3 I have subscribed to
FOSUserEvents::REGISTRATION_COMPLETED
to fill a second table with address data and writing a log.
namespace AppBundle\EventListener;
use AppBundle\Entity\Address;
use Doctrine\ORM\EntityManager;
use Psr\Container\ContainerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FilterUserResponseEvent;
class RegistrationSubscriber implements EventSubscriberInterface
{
private $em;
private $container;
public function __construct(EntityManager $em,ContainerInterface $container)
{
$this->em = $em;
$this->container = $container;
}
public static function getSubscribedEvents()
{
return [
FOSUserEvents::REGISTRATION_COMPLETED => [
['onRegistrationCompleted',0]
],
];
}
public function onRegistrationCompleted(FilterUserResponseEvent $event)
{
$user = $event->getUser();
$address = new Address();
$address->addAddress($user);
$this->em->persist($address);
$this->em->flush();
$this->container->get('app.logging')->write('Anmeldung',array('customer'=>$user->getId()));
}
}
services.yml:
app.registration_listener:
class: 'AppBundle\EventListener\RegistrationSubscriber'
arguments: ['#doctrine.orm.entity_manager']
tags:
- { name: kernel.event_subscriber}
app.logging:
class: AppBundle\Util\LogHandler
arguments: ['#doctrine.orm.entity_manager']
public: true
After submitting the registration form, FOSUserEvents::REGISTRATION_COMPLETED is triggered, but it is running twice, because address data and log data are written twice.
What can be the reason ?

I found it by myself.
In services.yml of symfony 3.3 I used the option
autoconfigure: true,
so services and event subscribers are loaded automatically.
That is why the event was triggered twice.

Related

Cannot autowire service in Symfony 3.4 and FosUserBundle

I try to override REGISTRATION_SUCCESS in FosUserBundle to redirect the admin on user's list after register a new user.
So I have created a new event subscriber :
<?php
namespace AppBundle\EventListener;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Mailer\MailerInterface;
use FOS\UserBundle\Util\TokenGeneratorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class RedirectAfterRegistrationSubscriber implements EventSubscriberInterface
{
private $mailer;
private $tokenGenerator;
private $router;
public function __construct(MailerInterface $mailer, TokenGeneratorInterface $tokenGenerator, UrlGeneratorInterface $router)
{
$this->mailer = $mailer;
$this->tokenGenerator = $tokenGenerator;
$this->router = $router;
}
public function onRegistrationSuccess(FormEvent $event)
{
$user = $event->getForm()->getData();
$user->setEnabled(false);
if (null === $user->getConfirmationToken()) {
$user->setConfirmationToken($this->tokenGenerator->generateToken());
}
$this->mailer->sendConfirmationEmailMessage($user);
$url = $this->router->generate('user_index');
$event->setResponse(new RedirectResponse($url));
}
public static function getSubscribedEvents()
{
return [
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess'
];
}
}
and the following service :
app.redirect_after_registration_subscriber:
class: AppBundle\EventListener\RedirectAfterRegistrationSubscriber
arguments: ['#fos_user.mailer', '#fos_user.util.token_generator', '#router']
tags:
- { name: kernel.event_subscriber }
I don't understand why this error appears :
Cannot autowire service "AppBundle\EventListener\RedirectAfterRegistrationSubscriber":
argument "$mailer" of method "__construct()" references interface
"FOS\UserBundle\Mailer\MailerInterface" but no such service exists. You should maybe alias
this interface to one of these existing services: "fos_user.mailer.default",
"fos_user.mailer.twig_swift", "fos_user.mailer.noop".
I suppose you are using autodiscovering of services. Something like:
# services.yaml
AppBundle\:
resource: '../src/'
...
So in addition to the #app.redirect_after_registration_subscriber that you define, Symfony defines another service with id #AppBundle\EventListener\RedirectAfterRegistrationSubscriber. Both point to AppBundle\EventListener\RedirectAfterRegistrationSubscriber class. Yet you configured the mailer parameter only for the first one.
The solution:
AppBundle\EventListener\RedirectAfterRegistrationSubscriber:
arguments: ['#fos_user.mailer', '#fos_user.util.token_generator', '#router']
tags:
- { name: kernel.event_subscriber }
With autowiring and autoconfigure you can even sypmlify to:
AppBundle\EventListener\RedirectAfterRegistrationSubscriber:
arguments:
$mailer: '#fos_user.mailer'

Symfony 4 : Request object change

i'm working on Symfony 4.
I have a service:
App\EventListener\EbayExceptionListener:
tags:
- { name: kernel.event_listener, event: kernel.controller }
arguments: [ "#doctrine.orm.entity_manager" , "#router" , "#session" ]
And here is my listener :
<?php
namespace App\EventListener;
use App\Entity\Ebay;
use Doctrine\ORM\EntityManager;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\Routing\Router;
class EbayExceptionListener
{
private $repository;
private $router;
private $session;
private $redirectName = 'home';
public function __construct(EntityManager $entityManager, Router $router, Session $session)
{
$this->repository = $entityManager->getRepository(Ebay::class);
$this->router = $router;
$this->session = $session;
}
//Check if an eBay entity exist in the database, if not redirect to the form to create the eBay entity
public function onKernelController(FilterControllerEvent $event)
{
$ebay = $this->repository->findOneBy(['name' => 'Ebay']);
$request = $event->getRequest();
$routeName = $request->get('_route');
dump($routeName);
if ($routeName != "home" AND $ebay == null) {
$this->session->getFlashBag()->add('error', 'Please provide information for Ebay form');
return new RedirectResponse($this->router->generate($this->redirectName));
}
}
}
My Controller:
<?php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class HomeController extends Controller
{
/**
* #Route("/", name="home")
*/
public function index()
{
return $this->render('home/index.html.twig', [
'controller_name' => 'HomeController',
]);
}
}
I have a problem with the var $routeName inside my Listener, in fact the value of this variable should be the name of the current route. But when I use the dump($routeName)
I have 2 results : "_wdt" and "home"
I don't know why the $request parameters are not the same when my listener is executed and after the page has been generated.
Because I have no page call "_wdt" it supposed to be "home".
Did I do something wrong ?
Thank you for your help.

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.

how to redirect after login success by using EventListener

I have a problem in the code of event listener because i need to make a EventListener when i login as type 1 ==>redirect to:
return $this->redirect($this->generateUrl('demands_patient'));
else redirect to another path .And this is the code of loginlistener
use Doctrine\ORM\Event\LifecycleEventArgs;
use Register\UserBundle\Entity\User;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class LoginListener implements EventSubscriberInterface
{
private $router;
public function __construct(UrlGeneratorInterface $router)
{
$this->router = $router;
}
public static function onSecurityInteractiveLogin()
{
return array(
FOSUserEvents::AUTHENTICATION_SUCCESS => 'redirectlogin',
);
}
public function redirectlogin(InteractiveLoginEvent $event)
{
$user = $event->getUser();
if($user->getType()=="1")
{
return $this->redirect($this->generateUrl('demands_patient'));
}
else
{
return $this->redirect($this->generateUrl('demands'));
}
}
}
and i add configuration in services.yml:
register_user.eventlistener.loginlistener:
class: Register\UserBundle\EventListener\LoginListener
arguments: [#router]
tags:
- { name: kernel.onSecurityInteractiveLogin }
Just today I faced a similar problem.
My solution was to use a Handler instead of a Listener, as explained in http://www.reecefowell.com/2011/10/26/redirecting-on-loginlogout-in-symfony2-using-loginhandlers/
What I did is:
In my bundle's `services.yml':
parameters:
cambra_comunitats.handler.user_login_redirect_handler.class: Cambra\ComunitatsBundle\Handler\UserLoginRedirectHandler
services:
cambra_comunitats.handler.user_login_redirect_handler:
class: "%cambra_comunitats.handler.user_login_redirect_handler.class%"
arguments: [#router, #security.context]
tags:
- { name: 'monolog.logger', channel: 'security' }
Then, I created the Cambra\ComunitatsBundle\Handler\UserLoginRedirectHandler class:
class UserLoginRedirectHandler implements \Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface
{
protected $router;
protected $security;
public function __construct(\Symfony\Component\Routing\Router $router, \Symfony\Component\Security\Core\SecurityContext $security)
{
$this->router = $router;
$this->security = $security;
}
public function onAuthenticationSuccess(\Symfony\Component\HttpFoundation\Request $request, \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token)
{
if ($this->security->isGranted('ROLE_ADMIN') && $this->security->getToken()->getUser()->isMustRedirect())
{
$response = new \Symfony\Component\HttpFoundation\RedirectResponse($this->router->generate('new_destination'));
} else
{
$referer_url = $this->router->generate('index_page');
$response = new \Symfony\Component\HttpFoundation\RedirectResponse($referer_url);
}
return $response;
}
}
And, finally, I enabled it on security.yml, inside of firewalls.main.form_login:
//.....
firewalls:
//...
main:
//...
form_login:
//...
success_handler: cambra_comunitats.handler.user_login_redirect_handler

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 }

Resources