Reputation: 14552
I have a Module
class with multiple "inline" event listeners in its onBootstrap(...)
:
...
class Module
{
public function onBootstrap(MvcEvent $mvcEvent)
{
...
$foo = ...;
...
$halPlugin = $viewHelperManager->get('Hal');
$halPlugin->getEventManager()->attach('bar', function ($event) {
...
});
...
$halPlugin->getEventManager()->attach('baz', function ($event) use ($foo)
{
...
});
...
}
...
}
Now, in order to hold/make Module#onBootstrap(...)
slim, I want to move the listeners from the closures to separate methods. It's no problem for the onBar
event listener. But it doesn't work for onBaz
, that needs additional input:
...
class Module
{
public function onBootstrap(MvcEvent $mvcEvent)
{
...
$halPlugin->getEventManager()->attach('bar', [$this, 'onBar']);
...
}
...
public function onBar()
{
...
}
public function onBaz() // <-- How to pass $foo to the method?
{
// Some stuff, that needs $foo...
...
}
}
In the original variant this input was being passed into the closure via the use
directive. But how to do this now?
How to move the logic of an event listener (attached in the Module#onBootstrap(...)
) from a closure with a use
statement to a separate method and pass arguments to it?
Upvotes: 1
Views: 151
Reputation: 745
What you can do is move the foo as property of your module class
and then access it with $this->foo
class Module
{
private $foo;
public function onBootstrap(MvcEvent $mvcEvent)
{
$this->foo = ...;
$halPlugin->getEventManager()->attach('baz', [$this, 'onBaz']);
...
}
...
public function onBaz()
{
$this->foo
// Some stuff, that needs $foo...
...
}
}
You can also make the function in module return a Closure where you use $foo
sent in with the function.
class Module
{
public function onBootstrap(MvcEvent $mvcEvent)
{
$foo = ...;
$halPlugin->getEventManager()->attach('baz', $this->onBaz($foo));
...
}
...
public function onBaz($foo)
{
return function ($event) use ($foo) {
$this->foo
// Some stuff, that needs $foo...
...
}
}
}
Upvotes: 1
Reputation: 11447
Move your method to a listener class.
A listener just needs to be Callable
, which means declaring the__invoke()
method in your class and placing all the code you have in your current onBaz()
method
<?php
namespace SomeModule\Listener;
class BazListener
{
protected $foo;
public function __construct($foo)
{
$this->foo = $foo;
}
public function __invoke()
{
// code from onBaz() method using $this->foo
}
}
You'll note that $foo is now a dependency requiring constructor injection. To fulfil the dependency write a factory for your listener class, fetch $foo
in the factory and inject it there.
<?php
namespace SomeModule\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class BazListenerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $services)
{
//fetch foo from wherever ..
$foo = ;
return new \SomeModule\Listener\BazListener($foo);
}
}
Now add the listener factory to the service manager so you can access it in onBootstrap
return array(
'service_manager' => (
'factories' => (
// ..
'SomeModule\Listener\BazListener' => 'SomeModule\Factory\BazListenerFactory',
// ..
),
),
);
Finally fetch your listener as a service from the service manager in onBootstrap and attach it to your event manager
public function onBootstrap(MvcEvent $mvcEvent)
{
$bazListener = $mvcEvent->getApplication()
->getServiceManager()
->get('SomeModule\Listener\BazListener');
$halPlugin->getEventManager()->attach('bar', $bazListener);
...
}
It's a bit of extra work, but it better separates concerns and you now have a standalone listener that you could potentially reuse instead of relying on a copy/pasted method from a Module class.
Upvotes: 2