Zend Framework 2 dispatch event doesn't run before action - php

I need some help. I want to run a method in Zend Framework 2 before the controller's action runs. I putted my method in Module.php's onBootstrap, but it doesn't run before action initated.
In Module.php:
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$app = $e->getApplication();
$em = $app->getEventManager();
$em->attach(MvcEvent::EVENT_DISPATCH, function($e) {
$controller = $e->getTarget();
$controller->Init();
});
}
I want to run the Init() method to my Adapter would be initialized before action runs but it didn't work and I always get this message:
Catchable fatal error: Argument 1 passed to Application\Model\Members::__construct() must be an instance of Zend\Db\Adapter\Adapter, null given, called in PATH\module\Application\src\Application\Controller\AdminController.php on line 39 and defined in PATH\module\Application\src\Application\Model\Members.php on line 17
The members class is in the action which should run and its __construct need to have a valid Adapter object that should be initialized in Init() method.
Could anyone help me?
Thanks a lot!

Try a different approach:
I'm assuming your controller extends the Zend\Mvc\Controller\AbstractActionController. Override the parent's onDispatch method in your controller, to do what you need to do:
ex:
class YourController extends AbstractActionController {
public function onDispatch($event){
$this->Init();
return parent::onDispatch($event);
}
//your other actions/init methods etc...
}

You need to set the priority > 1 when attaching to the event.
eg.
$em->attach(MvcEvent::EVENT_DISPATCH, function($e) {
$controller = $e->getTarget();
$controller->Init();
}, 100);
This ensures the code is executed pre-dispatch.

Related

Adding events to Laminas

I am trying to add event for Laminas Framework that will fire when \Laminas\Mvc\MvcEvent::EVENT_DISPATCH is triggered. But absolutelly nothing happends, like this triggers not exists. What am I doing wrong?
This is the code under the module\Application\src\Module.php:
use Laminas\ModuleManager\ModuleManager;
use Laminas\Mvc\MvcEvent;
class Module
{
public function init(ModuleManager $moduleManager)
{
ini_set("display_errors", '1');
$eventManager = $moduleManager->getEventManager();
$eventManager->attach(MvcEvent::EVENT_DISPATCH, [$this, 'onDispatch']);
}
public function onDispatch(\Laminas\EventManager\Event $event)
{
var_dump('ok');die;
}
}
I think you need use another method in Module it's should be something like this:
use Laminas\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $event)
{
$application = $event->getApplication();
$eventManager = $application->getEventManager();
$eventManager->attach(MvcEvent::EVENT_DISPATCH, [$this, 'onDispatch']);
}
public function onDispatch(MvcEvent $event)
{
var_dump('ok');
die;
}
}
In this case it onBootstrap. Hope help you
On init you'll need to get the shared event manager from the module manager:
<?php
use Laminas\ModuleManager\Feature\InitProviderInterface;
use Laminas\ModuleManager\ModuleManagerInterface;
use Laminas\Mvc\Application;
use Laminas\Mvc\MvcEvent;
final class Module implements InitProviderInterface
{
public function init(ModuleManagerInterface $manager): void
{
$sharedEventManager = $manager->getEventManager()->getSharedManager();
$sharedEventManager->attach(
Application::class,
MvcEvent::EVENT_DISPATCH,
function () {
var_Dump('dispatch from init');
}
);
}
}
The SharedEventManager is usually (or should be) shared between all event manager instances. This makes it possible to call or create events from other event manager instances. To differentiate between event names an identifier is used (so you can have more then one event with the same name). All MvcEvents belong to the Laminas\Mvc\Application identifier. Laminas\ModuleManager\ModuleManager has it's own EventManager instance, that is why you'll need to add the event to the SharedEventManager (init() is called by the ModuleManager and Laminas\ModuleManager\ModuleEvent is used).
onBootstrap() will be called by Laminas\Mvc\Application, that why you get the correct EventManager instance there.
As #Dimitry suggested: you should add that event in onBootstrap() as the dispatching process is part of the application and not the module manager. In init() you should only add bootstrap events.
And btw: you should use the Laminas\ModuleManager\Feature\* interfaces to make your application a bit more robust to future updates.

call controller action from module.php in zf2 onBootstrap

i am tracking the api error codes from module.php file. I want to pass the data to controller or want to create an adapter in module.php file.
Please suggest me different ways so that i can track and store error codes from module.php file itself.
i only want to exe a controller action and not want to redirect from module.php
Thanks in advance !
public function handleError(MvcEvent $evt)
{
$application = $evt->getApplication();
$eventManager = $application->getEventManager()->getSharedManager();
$eventManager->attach('Zend\Mvc\Controller\ApiTrackController', 'dispatch', function($event) {
$controller = $event->getTarget();
// Set public property
$controller->trackapi = 'trackapi';//ActionName
});//attach ends here.
}//Handle error function ends.

ZF2 how to listen to the dispatch event of a specific controller

How can I listen to the dispatch event of a specific controller? At the moment I do the following:
Module.php
public function onBootstrap(EventInterface $event) {
$application = $event->getApplication();
$eventManager = $application->getEventManager();
$serviceManager = $application->getServiceManager();
$eventManager->attach($serviceManager->get('MyListener'));
}
MyListener.php
class MyListener extends AbstractListenerAggregate {
public function attach(EventManagerInterface $eventManager) {
$this->listeners[] = $eventManager->attach(
MvcEvent::EVENT_DISPATCH, function($event) {
$this->setLayout($event);
}, 100
);
}
public function setLayout(EventInterface $event) {
$event->getViewModel()->setTemplate('mylayout');
}
}
This sets the layout for all controller dispatches. Now I want to set the layout only if the application dispatches a specific controller.
Like all Modules have an onBootstrap() method, all controllers extending AbstractController have an onDispatch() method.
Considering you want to apply a different layout for a single specific controller, you can simply do the following:
<?php
namespace MyModule\Controller;
use Zend\Mvc\Controller\AbstractActionController; // Or AbstractRestfulController or your own
use Zend\View\Model\ViewModel; // Or JsonModel or your own
use Zend\Mvc\MvcEvent;
class MyController extends AbstractActionController
{
public function onDispatch(MvcEvent $e)
{
$this -> layout('my-layout'); // The layout name has been declared somewhere in your config
return parent::onDispatch($e); // Get back to the usual dispatch process
}
// ... Your actions
}
You may do this for every controller that has a special layout. For those who don't, well, you don't have to write anything.
If you often need to change your layout (e.g. you have to handle not a single controller but several), you can attach an MvcEvent in your module.php to get your layout setting code in one place.
To keep things simple, I'm not using a custom listener here, but you may use one as well.
<?php
namespace MyModule;
use Zend\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e -> getApplication() -> getEventManager();
$eventManager -> attach(
MvcEvent::EVENT_DISPATCH,
// Add dispatch error event only if you want to change your layout in your error views. A few lines more are required in that case.
// MvcEvent::EVENT_DISPATCH | MvcEvent::EVENT_DISPATCH_ERROR
array($this, 'onDispatch'), // Callback defined as onDispatch() method on $this object
100 // Note that you don't have to set this parameter if you're managing layouts only
);
}
public function onDispatch(MvcEvent $e)
{
$routeMatch = $e -> getRouteMatch();
$routeParams = $routeMatch -> getParams();
switch ($routeParams['__CONTROLLER__']) {
// You may use $routeParams['controller'] if you need to check the Fully Qualified Class Name of your controller
case 'MyController':
$e -> getViewModel() -> setTemplate('my-first-layout');
break;
case 'OtherController':
$e -> getViewModel() -> setTemplate('my-other-layout');
break;
default:
// Ignore
break;
}
}
// Your other module methods...
}
You have to attach your event listener to the SharedEventManager and listen MvcEvent::EVENT_DISPATCH of the "Zend\Stdlib\DispatchableInterface" interface.
See an example:
$eventManager->getSharedManager()
->attach(
'Zend\Stdlib\DispatchableInterface',
MvcEvent::EVENT_DISPATCH,
$serviceManager->get('MyListener')
);
Within your listener you can get the instance of the target controller like so $controller = $event->getTarget();
So, eventually, the method "setLayout" may look like this:
public function setLayout(MvcEvent $event)
{
$controller = $event->getTarget();
if ($controller instanceof MyController)
{
$event->getViewModel()->setTemplate('mycontroller-layout');
}
}

Pass data to controller from Module.php

How can I pass data to controllers from Module class?
I need to pass data from onBootstrap method to all module controllers. What is the best way to do this. I can access controller using $e->getTarget() but don't know how to pass custom data to it. Maybe controller has storage for that?
The controller has access to the MvcEvent you can setup an event listener to attach arbitrary data to it and then fetch it within the controller.
Module.php
public function onBootstrap(MvcEvent $event)
{
$event->setParam('foo', 'bar');
}
Controller
public function fooAction() {
$foo = $this->getEvent()->getParam('foo', false);
}
#JonDay suggested an event listener which would also work well.
public function onBootstrap(MvcEvent $event)
{
$application = $event->getApplication();
$eventManager = $application->getEventManager()->getSharedManager();
$eventManager->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', function($event) {
$controller = $event->getTarget();
// Set public property
$controller->foo = 'bar';
// OR protected with setter
$controller->setFoo('bar');
});
}

Zend framework 2 translator in model

How to get translator in model?
Inside view we can get translator using this code
$this->translate('Text')
Inside controller we can get translator using this code
$translator=$this->getServiceLocator()->get('translator');
$translator->translate("Text") ;
But how to get translator in model?
I'd tried so many ways to get service locator in models
2 of those
1)Using MVC events
$e=new MvcEvent();
$sm=$e->getApplication()->getServiceManager();
$this->translator = $sm->get('translator');
if i pring $sm it is showing null. but it works fine in Model.php onBootstrap
2)Created one model which implements ServiceLocatorAwareInterface
SomeModel.php
<?php
namespace Web\Model;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class SomeModel implements ServiceLocatorAwareInterface
{
protected $services;
public function setServiceLocator(ServiceLocatorInterface $locator)
{
$this->services = $locator;
}
public function getServiceLocator()
{
return $this->services;
}
}
and used that inside my model
$sl = new SomeModel();
$sm=$sl->getServiceManager();
var_dump($sm); exit;
$this->translator = $sm->get('translator');
this is also printing null.
If you don't need the servicemanager instance in your model, simply inject translator instance to it.
For example:
// Your model's constructor
class MyModel {
// Use the trait if your php version >= 5.4.0
use \Zend\I18n\Translator\TranslatorAwareTrait;
public function __construct( $translator )
{
$this->setTranslator( $translator );
}
public function modelMethodWhichNeedsToUseTranslator()
{
// ...
$text = $this->getTranslator()->translate('lorem ipsum');
// ...
}
}
When you creating your model first time on service or controller level
class someClass implements ServiceLocatorAwareInterface {
public function theMethodWhichCreatesYourModelInstance()
{
// ...
$sm = $this->getServiceLocator();
$model = new \Namespace\MyModel( $sm->get('translator') )
// ...
}
}
If you need to instantiate your model (new MyModel();) on multiple methods/classes, consider to writing a factory for it.
Here is a nice article about Dependency Injection and PHP by Ralph Schindler for more detailed comments about this approach.
For your Model class to be ServiceLocatorAware, you not only need to implement the interface, you also need to make your model a service of the service manager, and fetch the model from there.
Add your model to the service manager, since it doesn't appear to need any constructor params, it's invokable, so you can add it to the invokables array in service manager config. You can do that by using the getServiceConfig() method in your Module class...
class Module
{
public function getServiceConfig()
{
return array(
'invokables' => array(
'SomeModel' => 'Fully\Qualified\ClassName\To\SomeModel',
),
);
}
}
Then, instead of calling the new keyword to create your model instance, you fetch it from the service manager, for instance, by calling the getServiceLocator() method in a controller action...
public function fooAction()
{
$sm = $this->getServiceLocator();
$model = $sm->get('SomeModel');
}
When your model is fetched from the service manager, a service initializer will look to see if it implements the ServiceLocatorAwareInterface and automatically call setServiceLocator() if it does, passing it an instance of the service manager.

Categories