fosuserbundle: set api key using event listner - symfony

I'm using symfony 2.3 and FosUserBundle ~2.0#dev. I want to set an api key for every user after a success registration. I implemented an event listner for this purpose but it doesn't work. I didn't find where is exactly the problem.
RegistrationConfirmListener
class RegistrationConfirmListener implements EventSubscriberInterface {
private $router;
private $em;
public function __construct(UrlGeneratorInterface $router, \Doctrine\ORM\EntityManager $em) {
$this->em = $em;
$this->router = $router;
}
/**
* {#inheritDoc}
*/
public static function getSubscribedEvents() {
return array(
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess'
);
}
public function onRegistrationSuccess(\FOS\UserBundle\Event\FormEvent $event) {
$user = $event->getForm()->getData();
// génération d'un unique key ,ici on a le id comme prefix donc garantie pas de duplication du key
$user->setApiKey(md5(uniqid($user->getId(), TRUE)));
$url = $this->router->generate('biginfo_admin_homepage');
$event->setResponse(new RedirectResponse($url));
}
}
service.yml
services:
biginfo_user.registration_complet:
class: Biginfo\UserBundle\EventListener\RegistrationConfirmListener
arguments: [#router,#doctrine.orm.entity_manager]
tags:
- { name: kernel.event_subscriber}

You should listen for FOSUserEvents::REGISTRATION_SUCCESS event. Because REGISTRATION_COMPLETED event listener method receives a FOS\UserBundle\Event\FilterUserResponseEvent instance, which is not fit for your situation.

Related

Dependency Injections seems to create new doctrine instance, which causes an entity to be detached)

I've got two services, those get Doctrine injected via the constructor. When loading an entity in an EventListener and giving it ot service the entity is detached.
When im providing the DoctrineEntityManager from an EventListener to service, the entity is still managed.
class Listener implements EventSubscriberInterface
{
/** #var EntityManagerInterface */
private $em;
/** #var Service */
private $service;
/** #var EventDispatcherInterface */
private $eventDispatcher;
public function __construct(
EntityManagerInterface $em,
Service $service,
EventDispatcherInterface $eventDispatcher
) {
$this->em = $em;
$this->eventDispatcher = $eventDispatcher;
$this->service = $service;
}
public function listenerFunction(Event $event)
{
$user = $event->getEntity()->getUser();
var_dump($this->em->contains($user)); // true
$this->service->func($this->em, $user);
}
}
class Service
{
/** #var EventDispatcherInterface */
private $eventDispatcher;
public function __construct(EntityManagerInterface $em, EventDispatcherInterface $eventDispatcher)
{
$this->em = $em;
$this->eventDispatcher = $eventDispatcher;
}
public function func($em, $user)
{
var_dump($this->em->contains($user)); // false
var_dump($em->contains($user)); // true
}
}
the services yaml
services:
_defaults:
autowire: true
autoconfigure: true
public: true
App\Payment\Command\:
resource: "%kernel.project_dir%/src/Payment/Command/*"
tags:
- { name: console.command }
App\Payment\Service\:
resource: "%kernel.project_dir%/src/Payment/Service/*"
App\Payment\Controller\:
resource: "%kernel.project_dir%/src/Payment/Controller/*"
App\Payment\EventSubscriber\:
resource: "%kernel.project_dir%/src/Payment/EventSubscriber/*"
tags:
- { name: kernel.event_subscriber }
The EntityManager in the service should contain the $user entity. Im thinking symfony is creating a second instance of the entitymanagerinterface here, but the says there is only one instance of each item (https://symfony.com/doc/current/service_container/shared.html)
Fixed by updating all packages (composer update) yay ;D

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.

Disable user after registration and allow admin to confirm it

There is a lot of questions at stack about this problem, but to be honest - no one is working for me (I am using Symfony 3.3 and FOSUserBundle 2.x).
What I need to achive:
user successfully registers at my page
his account is set automatically to default (no email is sent)
admin can activate/enable that account from his admin panel
I have created an EventListener but it won't work.. I can't even manipulate user object to change enabled status ...
#config.yml
fos_user:
db_driver: orm # other valid values are 'mongodb' and 'couchdb'
firewall_name: main
user_class: AppBundle\Entity\User
from_email:
address: "%mailer_user%"
sender_name: "%mailer_user%"
registration:
form:
type: AppBundle\Form\RegistrationType
And event listener:
class RegistrationListener implements EventSubscriberInterface
{
/**
* #var RouterInterface
*/
private $router;
private $em;
public function __construct(RouterInterface $route, UserManagerInterface $em)
{
$this->router = $route;
$this->em = $em;
}
/**
* {#inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess',
FOSUserEvents::REGISTRATION_CONFIRMED=> ['onRegistrationConfirmed', 999],
FOSUserEvents::REGISTRATION_FAILURE => 'onRegistrationFailure'
);
}
public function onRegistrationSuccess(FormEvent $event)
{
$url = $this->router->generate('app.registration.succes');
$response = new JsonResponse(['redirectUrl' => $url]);
$event->setResponse($response);
}
public function onRegistrationFailure(FormEvent $event)
{
$array = array('success' => false, 'message' => (string)$event->getForm()->getErrors(true, true));
$response = new JsonResponse($array, Response::HTTP_BAD_REQUEST);
$event->setResponse($response);
}
public function onRegistrationConfirmed(FilterUserResponseEvent $event)
{
/**
* this one is no working.. after registration account is enabled anyway...
*/
$user = $event->getUser();
$user->setEnabled(false);
$user->setSalt('test');
$this->em->updateUser($user);
}
}
Got it!
Just call it on FOSUserEvents::REGISTRATION_SUCCESS because FOSUserEvents::REGISTRATION_CONFIRMED is triggered only when user click activation link on his email, which I'm not sending to him.
class RegistrationListener implements EventSubscriberInterface
{
/**
* #var RouterInterface
*/
private $router;
private $em;
public function __construct(RouterInterface $route, UserManagerInterface $em)
{
$this->router = $route;
$this->em = $em;
}
/**
* {#inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess',
FOSUserEvents::REGISTRATION_FAILURE => 'onRegistrationFailure'
);
}
public function onRegistrationSuccess(FormEvent $event)
{
$user = $event->getForm()->getData();
$user->setEnabled(false);
$this->em->updateUser($user);
$url = $this->router->generate('app.registration.succes');
$response = new JsonResponse(['redirectUrl' => $url]);
$event->setResponse($response);
}
public function onRegistrationFailure(FormEvent $event)
{
$array = array('success' => false, 'message' => (string)$event->getForm()->getErrors(true, true));
$response = new JsonResponse($array, Response::HTTP_BAD_REQUEST);
$event->setResponse($response);
}
}

Symfony Service Configurator: getToken with security.context is NULL

I'm using Symfony Service Configurator in my project to configure a service after its instantiation (Docs),
in Configure method I need the current user logged so I inject the container and I tried to get the token form Security.context service but I got always NULL.
I tried also to inject only Security.context in my Configurator construct but I got same result.
Any ideas pls
Thanks.
class MyConfigurator
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function configure()
{
$user = $this->container->get('security.context')->getToken();
var_dump($user); // = NULL
}
}
I resolve the problem by getting the UserId from the session and fetch the current User from Database.
The UserId is set previously by a AuthenticationListener in my project.
So I modify my Configurator construct to be like this:
/**
* #param EntityManager $em
* #param Session $session
*/
public function __construct(EntityManager $em, Session $session)
{
$this->em = $em;
$this->session = $session;
}
A better way should be:
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
class MyConfigurator
{
private $tokenStorage;
public function __construct(EntityManager $em, TokenStorage $tokenStorage)
{
$this->em = $em;
$this->tokenStorage= $tokenStorage;
}
public function configure()
{
$user = $this->tokenStorage->getToken()->getUser();
}
....

How pass 'templating' component for service as argument?

I have a method in 'DynamicList' service that should return a select filled with dynamic data, but i'm getting "circular reference":
YML:
parameters:
my.dynamic_list.class: My\DynamicListBundle\Service\DynamicList
services:
my.dynamic_list:
class: %my.dynamic_list.class%
arguments: ['#doctrine.orm.default_entity_manager','#templating']
Class:
<?php
namespace My\DynamicListBundle\Service;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
class DynamicList
{
private $em;
private $templating;
public function __construct(
EntityManager $em,
EngineInterface $templating
) {
$this->em = $em;
$this->templating = $templating;
}
public function getSelect($slug)
{
$dynamic_list = $this->em
->getRepository('MyDynamicListBundle:DynamicList')
->findOneBy(array(
"slug" => $slug
));
return $this->templating->render('MyComponentsCoreBundle::Templates/DynamicList/combo.html.twig', array(
'dl' => $dynamic_list
));
}
}
I guess i don't need to put here the twig content: the problema occurs before.
Last, the error i'm getting:
Circular reference detected for service "my.dynamic_list", path: "my.dynamic_list -> templating -> twig". (500 Internal Server Error - ServiceCircularReferenceException)
What's the proper way to get templating component working in my service?
Well, I found a workaround but I don't if is the best way:
<?php
namespace My\DynamicListBundle\Service;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
class DynamicList
{
private $em;
private $templating;
public function __construct(
EntityManager $em
) {
$this->em = $em;
}
public function init(
EngineInterface $templating
) {
$this->templating = $templating;
}
public function getSelect($slug)
{
$dynamic_list = $this->em
->getRepository('MyDynamicListBundle:DynamicList')
->findOneBy(array(
"slug" => $slug
));
return $this->templating->render('MyComponentsCoreBundle::Templates/DynamicList/combo.html.twig', array(
'dl' => $dynamic_list
));
}
}
So, in controller, I call 'init()' to pass 'templating':
$dl_service = $this->get('my.dynamic_list');
$dl_service->init($this->container->get('templating'));

Resources