inject event to service - symfony

Is there a way to inject an event in a service in order to make the service kind of event agnostic?
In prose:
The service says: Hey, give me any Event and i will instanciate it and fire it after i have done my work.
I guess its difficult because the Events can be structured very different:
Event1 expects three parameters in the constructor and Event2 only one.
The purpose would be to reduce dependencies in a bundle and at the same time give it flexibilty for fe custom loggers to hook into the service.
How can i achieve something like this?
Maybe the other way around: fire an event of the service and make the listener event agnostic?

If the caller of the service does know about the arguments of the Event and the listener of the event does know about the event it would be possible to create the event.
I base my example on the example in the Symfony2 cook book: How to extend a Class without using Inheritance.
Normally you would dispatch an event like this:
$event = new HandleUndefinedMethodEvent($this, $method, $arguments);
$this->dispatcher->dispatch('foo.method_is_not_found', $event);
In your service you could add a method to create new events:
public function createEvent($name, $class, $arguments)
{
...
$eventClass = new \ReflectionClass($class);
$event = $eventClass->newInstanceArgs($arguments);
$this->dispatcher->dispatch($name, $event);
}
In this case the service does not need to know about the event, however, as mentioned above both invoker and the listener does have to know about.

Related

Symfony2 : addListener without using EventSubscriber class

I have a manager and inside a method.
Inside this method, I want to add a listener connect to Doctrine and use this :
$dispatcher = $this->container->get("event_dispatcher");
$dispatcher->addListener(Events::onFlush, function (Event $event) {
var_dump('test');die;
});
But in this case nothing append.
Do you have a solution to accomplish this ? I read a lot of time documentation about listener but I don't understand why didn't work :/
(and I don't want to use EventSubscriber class)
Thank you
Your code seems good, you have to use the doctrine event dispatcher and not the Symfony one.
$doctrineEventManager = $this->em->getEventManager();
$doctrineEventManager->addEventListener(Events::onFlush, New YourEventListenerClass()});;

Symfony/DRY - check if user is granted in every action

I'm using this code to check if a user is granted in my Symfony application :
$securityContext = $this->container->get('security.context');
if($securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED') ){
$user = $this->get('security.context')->getToken()->getUser()->getId();
} else {
return $this->render('IelCategoryBundle:Category:home.html.twig');
}
I have to ckeck this in almost every CRUD action that I'm writing (edit, delete, ...).
I feel not DRY at all (no play on words ;-)). Is there a better way to check this in many actions ?
JMSSecurityExtraBundle provides the #Secure annotation which eases checking for a certain user-role before invoking a controller/service method.
use JMS\SecurityExtraBundle\Annotation as SecurityExtra;
/** #SecurityExtra\Secure(roles="IS_AUTHENTICATED_REMEMBERED") */
public function yourAction()
{
// ...
}
Your best bet would be to rely on Kernel event listeners: http://symfony.com/doc/current/cookbook/service_container/event_listener.html.
Implement your listener as a service and then you could set $this->user to desired value if isGranted results TRUE. Later on, you could easily retrieve the value within the controller using:
$myServiceListener->getUser();
On the other hand, if check fails you could easily redirect user to your home.html.twig.

Adding Same FlashBag Code To Every Controller - Symfony2

I need to add a FlashBag code $session->getFlashBag()->add('foo', $bar); to every controller, along with the code required to get $bar. I am wondering if there is a better way then copying+pasting the code into every controller? Would there be some sort of master controller?
I'd recommend you to create a listener that will run before every controller that you indicate. Following this guide will show everything you need to set it up:
http://symfony.com/doc/2.0/cookbook/event_dispatcher/before_after_filters.html
http://symfony2.ylly.fr/symfony2-simulate-preexecute-postexecute-filters-actions-jordscream/
You should try implementing a service and registering it for onCoreController, then do $event->getController()->preAction() (or whatever function name you want...) , then you can implement those methods in the controllers that you need functionality in
something like
src/My/Bundle/RequestListener.php:
public function onCoreController(FilterControllerEvent $event) {
$evntController = $event->getController();
if (method_exists($evntController[0], 'beforeFilter')) {
$evntController[0]->beforeFilter();
}
}
Look here for more info
http://symfony.com/doc/2.0/book/internals.html#the-event-dispatcher
http://symfony.com/doc/2.0/book/internals.html
http://symfony.com/doc/current/cookbook/service_container/event_listener.html

Symfony2: Functional Testing for Controllers with Session Variables Set in security.interactive_login Event

I have implemented a listener on the security.interactive_login event to set one session variable that is used all over my app, as advised in Symfony2: After successful login event, perform set of actions. The problem is that the security.interactive_login event is not fired in functional tests (I guess this is logical since there is no actual interactive login). Given this restriction: How can I run functional tests with session variables set upon user login?
If u use basic authentication you can eventualy manualy fire event handler for testing but i think more proper way is to log in to app throu login form with submiting it with client - event listener should be fired that way.
Just to bring detail on the workaround:
In the functional test case I changed
$client = static::createClient(array(), array(
'PHP_AUTH_USER'=>'alberto'
,'PHP_AUTH_PW'=>'********'
)
);
For
$client = static::createClient();
$crawler = $client->request('GET','/secured/login');
$form = $crawler->selectButton('Aceptar')->form();
$form['_username'] = 'alberto';
$form['_password'] = '********';
$crawler = $client->submit($form);

Robotlegs Flex - How deal with mediators being initialized after creationComplete?

I'm trying to build a Flex application using the Robotlegs framework and don't know how to deal with the creation of mediators (-> onRegister being called) only after the creationComplete event of the view component.
My application loads several XML files at startup, models are created from the files and then dispatch events with the data.
Problem: When "loading" embedded/local files at startup, the model events are dispatched way before the mediators listeneres are added, although they're mapped before triggering the initial data load in the main context.
Is anybody using robotlegs with flex and has foud a "cleaner" way around this, than manually dispatching an event in the mediators onRegister? As doing so the "automatic" mediation would not really be automatic anymore ...
Edit:
Minimal example code:
Context:
override public function startup( ):void{
mediatorMap.mapView( EditorMenu, EditorMenuMediator );
commandMap.mapEvent( ContextEvent.STARTUP, LoadConfigurationCommand );
dispatchEvent( new ContextEvent( ContextEvent.STARTUP ) );
}
LoadConfigurationCommand:
[Inject] public var configurationService:IXMLLoader;
override public function execute():void{
configurationService.loadXML();
}
ConfigurationService:
public function loadXML(){
trace( "xml loaded" );
dispatch( new XMLLoadedEvent( XMLLoadedEvent.CONFIGURATION_LOADED, result ) );
}
EditorMenuMediator:
override public function onRegister( ):void{
trace( "menu onregister" );
addContextListener( XMLLoadedEvent.CONFIGURATION_LOADED, handleXmlLoaded, XMLLoadedEvent);
}
The trace "menu onregister" is happening way before the trace "xml loaded", so the mediator's not listening when the XmlLoadedEvent is dispatched.
I approach this with the StateMachine and grab control of the order of operations. Another approach here would be to add the listener, but also inject the model and check it for data in onRegister. Either approach should put you in front of the race conditions.
I'm not expert in RobotLegs (I'm actually a Parsley addict), however, if you're using a Mediator pattern, that means that your mediator has direct access to the view itself. How about you create an interface class for all your views that has an init public function which your mediator could call after the onRegister.
Maybe Robotlegs has another way of doing it with metadata or events or something, but this is still valid.

Resources