Symfony2 Listeners and Sessions - session

I'm trying to set data of a session in a Symfony2 Listener (Symfony v. 2.0.16) but somehow the listener won't register the session variables.
I have the following:
My services.yml entry:
kernel.listener.domain_listener:
class: Etiam\ClubWebBundle\Listener\SubdomainListener
arguments: [#service_container]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onDomainParse }
And here's my listener:
namespace Etiam\ClubWebBundle\Listener;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
class SubdomainListener {
private $container;
public function __construct($container)
{
$this->container = $container;
}
public function onDomainParse(Event $event) {
$session = $this->container->get('session');
$session->set('siteData', '123');
$session->save();
}
}
Can anyone tell me why my session data isn't being saved when the listener is being accessed?

Okay, I figured it out.
If anyone ever comes across something similar with Listeners that you're changing things and nothing happens:
If you have multiple bundles you've maybe got 2 identical listeners and you're editing the wrong one. Listeners are apparently global, and you need only one to be valid across multiple bundles.
You can see your loaded listeners in the profiler under events.

Related

Laravel 5.6 one event and many optional listeners

in Laravel 5.6
I have an event named DocumentSend,
And i have many Listeners Like (SendEmail, SendNotification, SendSMS),
listeners are optional (depends on document type and defined by user),
now the question is:
How can i call for example DocumentSend event with just SendSMS listener or DocumentSend with all the listeners?
I hope you get my mean,and tell me the best practice for my issue.
Thanks in advance
Well, the simple answers is - you can't. When you fire event all registered listeners will listen to this event and all of them will be launched.
However nothing stops you to prevent running code from listener.
For example you can fire event like this:
event(new DocumentSend($document, true, false, false));
and define constructor of DocumentSend like this:
public function __construct($document, $sendEmail, $sendNotification, $sendSms)
{
$this->document = $document;
$this->sendEmail = $sendEmail;
$this->sendNotification = $sendNotification;
$this->sendSms = $sendSms;
}
and now in each listener you can just verify correct variable, so for example in SendEmail listener in handle you can do it like this:
public function handle(DocumentSend $event)
{
if (!$event->sendSms) {
return;
}
// here your code for sending
}
similar you can do for other listeners.
Of course this is just example - you don't have to use 4 variables. You can set some properties to $document only to mark how it should be sent.

Disabling eventListener on doctrine onFlush while loading fixtures

I have to persist new entities when app users make modifications on a bunch of existing entities (traceability issue). I created an EventListener on Doctrine onFlush event. The problem is: This is not supposed to happen on fixtures loading.
I did this to prevent fixtures from triggering the listener but I wonder if it is a good solution:
In my services.yaml:
App\DataFixtures\:
class: App\DataFixtures\LoadFixtures
tags: [name: doctrine.fixture.orm]
arguments:
- '#doctrine.orm.default_entity_manager.event_manager'
In my App\DataFixtures\LoadFixtures:
public function __construct(EventManager $eventManager)
{
$this->eventManager = $eventManager;
}
public function load(ObjectManager $manager)
{
$historizationManager = null;
foreach ($this->eventManager->getListeners() as $event =>$listeners){
foreach ($listeners as $key => $listener){
if($listener instanceof HistorizationManager){
$historizationManager = $listener;
}
}
}
if($historizationManager){
$this->eventManager->removeEventListener(array('onFlush'),$onFlushHistoryListener);
}
// doing some work
}
This is the simplest solution I've come with, please let me know if there is something wrong with that.
If this is still an issue, you could try the following:
add an enabled flag to the EventSubscriber class, default true
use autowiring to make that subscriber available in your fixture loader
set the flag to false before loading the first fixture
finally, check for that flag before handling the event
But if you've found a better way, I'd be happy to hear from you
To clarify about how to set the flag to false: you are usually running your tests in the test environment. By using a configuration file like config_test.yaml, you can override the default service configuration like:
services:
App\EventSubscribers\YourSubscriber:
calls:
- ['setEnabled', [false]]

Yii2 session event before close/destroy

I want to run some code every time before user session is being destroyed for any reason. I haven't found any events binded to session in official documentation. Has anyone found a workaround about this?
There are no events out of the box for Session component.
You can solve this problem with overriding core yii\web\Session component.
1) Override yii\web\Session component:
<?php
namespace app\components;
use yii\web\Session as BaseSession
class Session extends BaseSession
{
/**
* Event name for close event
*/
const EVENT_CLOSE = 'close';
/**
* #inheritdoc
*/
public function close()
{
$this->trigger(self::EVENT_CLOSE); // Triggering our custom event first;
parent::close(); // Calling parent implementation
}
}
2) Apply your custom component to application config:
'session' => [
'class' => 'app\components\Session' // Passing our custom component instead of core one
],
3) Attach handler with one of available methods:
use app\components\Session;
use yii\base\Event;
Event::on(Session::className(), Session::EVENT_OPEN, function ($event) {
// Insert your event processing code here
});
Alternatively you can specify handler as method of some class, check official docs.
As an alternative to this approach, take a look at this extension. I personally didn't test it. The Yii way to do it I think will be overriding with adding and triggering custom events as I described above.

Zend framework 2 : Add different authentication adapter for two different modules

I have two different modules. Now I need to add different authentication mechanism for both modules.
So I added event code first module's Module.php's onBootstrap method
$listener = $serviceManager->get('First\Service\AuthListener');
$listener->setAdapter($serviceManager->get('First\Service\BasicAuthAdapter'));
$eventManager->attach(MvcEvent::EVENT_ROUTE, $listener, 0);
and in second module's Module.php's onBootstrap method
$listener = $serviceManager->get('Second\Service\AuthListener');
$listener->setAdapter($serviceManager->get('Second\Service\AdvAuthAdapter'));
$eventManager->attach(MvcEvent::EVENT_ROUTE, $listener, 0);
Now if I disable one of modules, functionality working fine and request properly authenticated. While enabling both module do some kind of overlapping So even required module properly authenticated, But other module event code also got executed and system give not authenticated error.
I am thinking this due to event handler code in both module.php is executed without take care of requested module url.
I can verify with requested route pattern before authentication, But that is look like a hack instead of good solution.
If better solution exists for handling this issue ?
UPDATE :
My AuthListener Code :
namespace First\Service;
use Zend\Authentication\Adapter\AdapterInterface;
use Zend\Mvc\MvcEvent;
class AuthListener
{
protected $adapter;
public function setAdapter(AdapterInterface $adapter)
{
$this->adapter = $adapter;
}
public function __invoke(MvcEvent $event)
{
$result = $this->adapter->authenticate();
if (!$result->isValid()) {
$response = $event->getResponse();
// Set some response content
$response->setStatusCode(401);
$routeMatch = $event->getRouteMatch();
$routeMatch->setParam('controller', 'First\Controller\Error');
$routeMatch->setParam('action', 'Auth');
}
}
}
There is a good way to make module specific bootstrap - to use SharedManager:
$e->getApplication()->getEventManager()->getSharedManager()
->attach(__NAMESPACE__, 'dispatch', function(MvcEvent $e) {
// This code will be executed for all controllers in current __NAMESPACE__
}, 100);
Here is a good article to understand difference between EventManager and SharedEventManager
There is no additional info about listeners in the question, but I try to guess:
If you use as listener some callable class - it's ok, just replace function() { } by your $listener.
If you use as listener some class, that implements
ListenerAggregateInterface, you should convert listeners to
SharedListenerAggregateInterface and use method attachAggregate
instead of attach
I hope it helps!

How to get a reference to the service manager inside the Module's init method (ZF2)?

I need that some code be executed before any MvcEvent::EVENT_BOOTSTRAP listener get execute. Evidently Module::onBootstrap is no an option. I end with the following code:
class Module
{
function init(\Zend\ModuleManager\ModuleManager $moduleManager)
{
$moduleManager->getEventManager()->attach(
MvcEvent::EVENT_BOOTSTRAP, array(ClassX, 'StaticMethodOfClassX'), 20000);
}
}
I don't want have hard code the array(ClassX, 'StaticMethodOfClassX') reference but get it from the service manager. My problem is that I don't know how to get an service manager reference inside the module's init method. Any help? or this is impossible in ZF2 right now? Whatever variant to this schema or opinion will be appreciate too ;)
EDIT:
I will clarify "Evidently Module::onBootstrap is no an option", cos may be is not so trivial ;)
Modules Module::onBootstrap methods are executed when the event MvcEvent::EVENT_BOOTSTRAP is triggered, but the attachment of each module's Module::onBootstrap method to that event depend of the order in which modules were loaded. Due to, the order in which a specific Module::onBootstrap method will be executed depend on what other modules exist and how other modules affect the order in which that specific module will be loaded. Beside, whatever listener attached to the MvcEvent::EVENT_BOOTSTRAP event with priority greater than 1 will be execute before any module Module::onBootstrap method, example the ViewManager::onBootstrap listener. So, to achieve what I want
I need that some code be executed before any
MvcEvent::EVENT_BOOTSTRAP listener get execute
modules obBootstrap methods are not an option.
This is a very old post but since no answer has been accepted and I recently needed to achieve the same thing, I thought I'd share my solution.
The reason I needed to access the ServiceManager before the Bootstrap event is triggered, was so I could manipulate the merged configuration with values retrieved from the database.
Problem:
The example found in the Zend documentation shows how to manipulate the merged configuration, but at that particular time the Service manager is empty, making it impossible to retrieve things like database adapters etc.
Solution:
In your module class, implement the interface InitProviderInterface and add the appropriate method.
public function init(ModuleManagerInterface $moduleManager)
{
$eventManager = $moduleManager->getEventManager();
$eventManager->attach(ModuleEvent::EVENT_LOAD_MODULES_POST, [$this, 'onLoadModulesPost']);
}
The EVENT_LOAD_MODULES_POST event will get invoked after the EVENT_MERGE_CONFIG event but before the EVENT_BOOTSTRAP event is triggered. Also at this particular time the ServiceManager will contain all the factories, invokable classes you're wanting to access.
Your callback method may look something like.
public function onLoadModulesPost(ModuleEvent $event)
{
/* #var $serviceManager \Zend\ServiceManager\ServiceManager */
$serviceManager = $event->getParam('ServiceManager');
$configListener = $event->getConfigListener();
$configuration = $configListener->getMergedConfig(false);
$someService = $serviceManager->get('Your/Custom/Service');
$information = $someService->fetchSomeInformation();
$configuration = array_merge($configuration, $information);
$configListener->setMergedConfig($configuration);
$event->setConfigListener($configListener);
$serviceManager->setAllowOverride(true);
$serviceManager->setService('Config', $configuration);
$serviceManager->setAllowOverride(false);
}
You can get it off the MvcEvent
$locator = $event->getTarget()->getServiceLocator()->get('YourObject')
If you don't have access to the event, you can set the event as a property on the Module class on bootstrap, and then use it in your init method whenever.
public function onBootstrap($event) {
$this->setMvcEvent($event);
}
function init(\Zend\ModuleManager\ModuleManager $moduleManager)
{
$locator = $this->mvc_event->getTarget()->getServiceLocator()->get('YourClass');
$moduleManager->getEventManager()->attach(
MvcEvent::EVENT_BOOTSTRAP, array(ClassX, 'StaticMethodOfClassX'), 20000);
}
Are you using ZfcBase in your application? The AbstractModule has a boostrap method (not onBootstrap) which is executed by this event handler in the init method
$sharedManager->attach('Zend\Mvc\Application', 'bootstrap', function($e) use ($instance, $moduleManager) {
$app = $e->getParam('application');
...
$instance->bootstrap($moduleManager, $app);
});
Of course you can use this approach without ZfcBase.
Then you can implement Zend\ServiceManager\ServiceLocatorAwareInterface:
public function bootstrap(\Zend\ModuleManager\ModuleManager $moduleManager, \Zend\Mvc\ApplicationInterface $app){
$this->setServiceLocator($app->getServiceManager());
parent::bootstrap($moduleManager, $app);
}
public function setServiceLocator(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator){
$this->_serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator(){
return $this->_serviceLocator;
}
Maybe a bit later but hope it will help somebody else. At the init point there is no much services at the Service Manager but you can access it:
public function init(ModuleManager $moduleManager)
{
$sm = $moduleManager->getEvent()->getParam('ServiceManager');
$applicationConfig = $sm->get('applicationconfig');
var_dump($applicationConfig['modules']);
}
In this case we are retrieving the module names.
This is better idea.
class module
public function onBootstrap(MvcEvent $e)
{
$sm = $app->getServiceManager();
$config = $sm->get('config');
and this is all.

Resources