Print all routes from ZF2 modules - php

I'm trying to print all routes from my modules on "some page" with var_dump() or whatever debug function.
I have found lots of posts and samples but I can't get them printed and most examples fail in my code.
So far I think this is the best way to do so but where to use this code ?
// $sl instanceof Zend\ServiceManager\ServiceManager
$config = $sl->get('Config');
$routes = $config['router']['routes'];
If you want to view all routes just for debugging purposes, you can use var_dump or similar on the router object:
// $sl instanceof Zend\ServiceManager\ServiceManager
$router = $sl->get('Router');
var_dump($router);

You may print all routes from in your controller's method. Look at the following example
module/Application/src/Application/Controller/IndexController.php
<?php
namespace Application\Controller;
use Zend\View\Model\ViewModel;
use Zend\Mvc\Controller\AbstractActionController;
class IndexController extends AbstractActionController
{
/**
* #var array
*/
protected $routes;
/**
* #param array $routes
*/
public function __construct(array $routes)
{
// Here is the catch
$this->routes = $routes;
}
public function indexAction()
{
// Thus you may print all routes
$routes = $this->routes;
echo '<pre>';
print_r($routes);
echo '</pre>';
exit;
return new ViewModel();
}
}
As we passed an array of routes to the constructor of IndexController. We need to make an factory of this controller. A factory is a class that creates instances of other classes.
module/Application/src/Application/Controller/IndexControllerFactory.php
<?php
namespace Application\Controller;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class IndexControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$serviceManager = $serviceLocator->getServiceLocator();
$config = $serviceManager->get('Config');
$routes = $config['router'];
return new IndexController($routes);
}
}
A invokable class can not be constructed with arguments. Our controller would not work as invokables because we know we already passed an argument to its constructor. So we need to configure that in factories key under controllers key of our module.config.php
module/Application/config/module.config.php
'controllers' => [
'invokables' => [
// This would not work any more as we created a factory of it
// 'Application\Controller\Index' => 'Application\Controller\IndexController',
],
// We should do it thus
'factories' => [
'Application\Controller\Index' => 'Application\Controller\IndexControllerFactory',
],
],
This answer has been edited for good practice as #av3 suggested!

Related

ServiceManager in ZF3

I know that this has been covered extensively in other threads, but I'm struggling to work out how to replicate the effect of $this->getServiceLocator() from ZF2 controllers in ZF3 ones.
I have tried creating a factory using the various other answers and tutorials that I've found here and elsewhere, but ended up in a mess with each of them, so I'm pasting my code as it was when I started in the hope that someone can point me in the right direction?
From /module/Application/config/module.config.php
'controllers' => [
'factories' => [
Controller\IndexController::class => InvokableFactory::class,
],
],
From /module/Application/src/Controller/IndexController.php
public function __construct() {
$this->objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
$this->trust = new Trust;
}
You can not use $this->getServiceLocator() in controller any more.
You should add one more class IndexControllerFactory where you will get dependencies and inject it in IndexController
First refactor your config:
'controllers' => [
'factories' => [
Controller\IndexController::class => Controller\IndexControllerFactory::class,
],
],
Than create IndexControllerFactory.php
<?php
namespace ModuleName\Controller;
use ModuleName\Controller\IndexController;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
class IndexControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container,$requestedName, array $options = null)
{
return new IndexController(
$container->get(\Doctrine\ORM\EntityManager::class)
);
}
}
At the end refactor you IndexController to get dependencies
public function __construct(\Doctrine\ORM\EntityManager $object) {
$this->objectManager = $object;
$this->trust = new Trust;
}
You should check official documentation zend-servicemanager and play around a little bit...
Whilst the accepted answer is correct, I will implement mine a bit differently by injecting the container into the controller and then get other dependencies in constructor like so...
<?php
namespace moduleName\Controller\Factory;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use moduleName\Controller\ControllerName;
class ControllerNameFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return new ControllerName($container);
}
}
Your controller should look something like this:
namespace ModuleName\Controller;
use Doctrine\ORM\EntityManager;
use Zend\ServiceManager\ServiceManager;
class ControllerName extends \App\Controller\AbstractBaseController
{
private $orm;
public function __construct(ServiceManager $container)
{
parent::__construct($container);
$this->orm = $container->get(EntityManager::class);
}
In your module.config, be sure to register the factory like so:
'controllers' => [
'factories' => [
ControllerName::class => Controller\Factory\ControllerNameFactory::class,
],

Zend Expressive, get service from Php Plates template

I use Zend Expressive framework via default ZE skeleton app with Zend ServiceManager as DIC and Plates as template engine.
Let's say I've got index.phtml template. I want to get some service, which dumps me assets, smth like:
<?= $this->getContainer()->get('my service class')->dumpAssets() ?>
Service is registered via factory and accesible in the app:
<? $container->get('my service class') ?>
How to pass external service instance or its result into template?
It's pretty much bad practice to inject the entire service container into a template (or any other class except a factory). A better approach would be to write an extension to dump the assets.
Extension class:
<?php
namespace App\Container;
use League\Plates\Engine;
use League\Plates\Extension\ExtensionInterface;
use App\Service\AssetsService;
class DumpAssetsExtension implements ExtensionInterface
{
public $assetsService;
/**
* AssetsExtension constructor.
* #param $container
*/
public function __construct(AssetsService $assetsService)
{
$this->assetsService = $assetsService;
}
public function register(Engine $engine)
{
$engine->registerFunction('dumpAssets', [$this, 'dumpAssets']);
}
public function dumpAssets()
{
return $this->assetsService->dumpAssets();
}
}
Factory:
<?php
namespace App\Container;
use Interop\Container\ContainerInterface;
class DumpAssetsFactory
{
public function __invoke(ContainerInterface $container)
{
$assetsService = $container->get(App\Service\AssetsService::class);
return new PlatesExtension($assetsService);
}
}
Configuration:
<?php
return [
// ...
'factories' => [
App\Container\DumpAssetsExtension::class => App\Container\DumpAssetsFactory::class,
]
];
In your template:
<?php
$service = $this->dumpAssets();
?>
I figured out how to access container from template engine via extensions. It's not clear MVC-ly, but...
At first, add plates config into config/autoload/templates.global:
return [
// some othe settings
'plates' => [
'extensions' => [
App\Container\PlatesExtension::class,
],
],
],
At second, create App\Container\PlatesExtension.php with code:
<?php
namespace App\Container;
use League\Plates\Engine;
use League\Plates\Extension\ExtensionInterface;
class PlatesExtension implements ExtensionInterface
{
public $container;
/**
* AssetsExtension constructor.
* #param $container
*/
public function __construct($container)
{
$this->container = $container;
}
public function register(Engine $engine)
{
$engine->registerFunction('container', [$this, 'getContainer']);
}
public function getContainer()
{
return $this->container;
}
}
At third, create factory App\Container\PlatesExtensionFactory.php to inject container into plates extension:
<?php
namespace App\Container;
use Interop\Container\ContainerInterface;
class PlatesExtensionFactory
{
public function __invoke(ContainerInterface $container)
{
return new PlatesExtension($container);
}
}
Next, register plates extension in ServiceManager (config/dependencies.global.php):
return [
// some other settings
'factories' => [
App\Container\PlatesExtension::class => App\Container\PlatesExtensionFactory::class,
]
];
At last, get container and needed service from Plates template:
<?
$service = $this->container()->get('my service class');
?>

how can I pass a variable to partial navigation script in zf2?

I've made a global variable in bootstrap of Module.php
public function setCashServiceToView($event) {
$app = $event->getParam('application');
$cashService = $app->getServiceManager()->get('Calculator/Service/CashServiceInterface');
$viewModel = $event->getViewModel();
$viewModel->setVariables(array(
'cashService' => $cashService,
));
}
public function onBootstrap($e) {
$app = $e->getParam('application');
$app->getEventManager()->attach(\Zend\Mvc\MvcEvent::EVENT_RENDER, array($this, 'setCashServiceToView'), 100);
}
I can use it inside of my layout.phtml as
$this->cashService;
But I need this variable to use in my partial script of navigation menu, which I call in layout.phtml:
echo $this->navigation('navigation')
->menu()->setPartial('partial/menu')
->render();
?>
How can I use it inside of my partial/menu.phtml? And may be there is a better way, than to declare it in onBootstrap function?
Thank you for your answers. I decided to make an extended class of \Zend\View\Helper\Navigation\Menu to provide there a property of cashService. However I receive an error:'Zend\View\Helper\Navigation\PluginManager::get was unable to fetch or create an instance for Calculator\Service\CashServiceInterface'.
I need this service to display navigation menu. Seems weird, but that's true. I display some diagram in it, using the data, which I get from the service. So why do I have the error?
I added to module.config.php
'navigation_helpers' => array(
'factories' => array(
'mainMenu' => 'Calculator\View\Helper\Factory\MainMenuFactory'
),
MainMenuFactory:
namespace Calculator\View\Helper\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Calculator\View\Helper\Model\MainMenu;
Class MainMenuFactory implements FactoryInterface {
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator) {
return new MainMenu(
$serviceLocator->get('Calculator\Service\CashServiceInterface')
);
}
P.S: CashServiceInterface is an alias to CashServiceFactory
You could remove the event listener and use a custom view helper to access the service in the view.
namespace Calculator\View\Helper;
use Zend\View\Helper\AbstractHelper;
class CashService extends AbstractHelper
{
protected $cashService;
public function __construct(CashServiceInterface $cashService)
{
$this->cashService = $cashService;
}
public function __invoke()
{
return $this->cashService;
}
}
Create a factory.
namespace Calculator\View\Helper;
class CashServiceFactory
{
public function __invoke($viewPluginManager)
{
$serviceManager = $viewPluginManager->getServiceLocator();
$cashService = $serviceManager->get('Calculator\\Service\\CashServiceInterface');
return new CashService($cashService);
}
}
Register the new helper in moudle.config.php.
'view_helpers' => [
'factories' => [
'CashService' => 'Calculator\View\Helper\CashServiceFactory',
],
],
Then you can use the plugin in all view scripts.
$cashService = $this->cashService();

ZF2 service locator interface does not return an object

I am trying to create a model that will have access to the ZF2 service locator.
I have a model class that looks like this:
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class testDelete implements ServiceLocatorAwareInterface
{
protected $services;
/**
* construct function
*/
public function __construct ()
{
$router = $this->getServiceLocator()->get('Router');
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
}
In reading the tutorials and blogs this should give me an instance of the service locator, which I can then use to call classes. But when I call it I get a message
Fatal error: Call to a member function get() on a non-object...
Does anyone know why this would be?
Do I need to some sort of setup to get the service locator interface to work?
Because your class implements ServiceLocatorAware, the service manager will automatically inject the service locator into it. However, it can only do that if the service manager is the thing instantiating the testDelete class. So you need to setup a service for testDelete.
Once you've done that, you still won't be able to call $this->getServiceLocator() from __construct(), as the dependency won't have been injected into the class yet.
If all you want is to get the router into your testDelete class, just create a service for testDelete and pass the router in as a dependency. This would be much easier than what you're currently trying to do.
It looks like you need to set the "Router" service in the getServiceConfig() method of your Modules Module.php file:
public function getServiceConfig()
{
return array
(
'factories' => array
(
'Router' => function($serviceManager)
{
... your logic ...
return $Router
},
),
);
}
Your Module.php file will look something like this:
namespace ModuleName;
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
/**
* Tables (& Databases) Used in this Module
*/
use Your\Model\ModelName;
use Your\Mapper\ModelMapperName;
class Module implements AutoloaderProviderInterface
{
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$serviceManager = $e->getApplication()->getServiceManager();
$sharedManager = $e->getApplication()->getEventManager()->getSharedManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
/**
* Additional logic for Setting up Logging, Pre-Initializations, Exceptions etc
*/
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getAutoloaderConfig()
{
return array
(
'Zend\Loader\ClassMapAutoloader' => array
(
include __DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array
(
'namespaces' => array
(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getServiceConfig()
{
return array
(
'factories' => array
(
'Router' => function($serviceManager)
{
... your logic ...
return $Router
},
),
);
}
}

ZF2: How to get Zend\Navigation inside custom route?

I have custom router and I have to get access to Zend\Navigation inside this custom router. I was googling, asking and searching and no results :/
All I need is to find nodes with 'link' param using Zend\Navigation in my Alias::match function.
Here is my module.config.php:
'navigation' => array(
'default' => array(
'account' => array(
'label' => 'Account',
'route' => 'node',
'pages' => array(
'home' => array(
'label' => 'Dashboard',
'route' => 'node',
'params' => array(
'id' => '1',
'link' => '/about/gallery'
),
),
),
),
),
),
[...]
And here is my Alias class:
// file within ModuleName/src/ModuleName/Router/Alias.php
namespace Application\Router;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Mvc\Router\Http;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Alias extends Http\Segment implements ServiceLocatorAwareInterface
{
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function match(Request $request, $pathOffset = null)
{
[...]
return parent::match($request, $pathOffset);
}
}
EDITED:
Now i know that I should inject service manager into my custom router. Let me know if You know how to do this :)
EDITED:
Ok, its not custom router but route. My bad. I was talking on #zftalk irc chanell and AliasSegment class should implements ServiceLocatorAwareInterface. Ok I've tried it but now there is another problem.
In setServiceLocator function i can't get service locator. It returns null object, however $serviceLocator is class Zend\Mvc\Router\RoutePluginManager.
public function setServiceLocator(ServiceLocatorInterface $serviceLocator){
$sl = $serviceLocator->getServiceLocator();
var_dump($sl); // NULL
}
Any ideas how to get Zend navigation from it ?
EDITED
Corresponding to what #mmmshuddup said, I've changed my custom router class. (New version is above). Also in my Module.php, within onBootstrap function, I added this line:
$sm->setFactory('Navigation', 'Zend\Navigation\Service\DefaultNavigationFactory', true);
Navigation works and its instantiated before route so it should be visible within my Alias class but it's not.
I've put into my match function in Alias class this line:
$servicesArray = $this->getServiceLocator()->getRegisteredServices();
and $servicesArray is almost empty. There is no service, no factories. The same line inserted into onBootstrap, just after setting new factory (as above) returns array with navigation and other services.
The question is: how can i share this array (or ServiceManager) with my custom router: Alias ?
I have to say that all I want to do was possible in ZF1 and it was quite easy.
EDIT
I found a solution. The answer is below
That is because the object itself really doesn't have any properties declared. But if you do this:
echo get_class($sl);
You will see that it is indeed an instance of Zend\ServiceManager\ServiceManager
You should be able to get your navigation instance by doing something like:
$nav = $sl->get('Navigation');
EDIT:
I just notice you have some stuff in the wrong location of your code. You're calling getServiceLocator() on $serviceLocator which is already the instance of that. Also you're calling it within setServiceLocator(). You should change it to:
// EDIT - file within ModuleName/src/Router/Alias.php
namespace Application\Router;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Mvc\Router\Http;
class Alias extends Http\Segment implements ServiceLocatorAwareInterface
{
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function match(Request $request, $pathOffset = null)
{
$nav = $this->getServiceLocator()->get('Navigation');
// ...
return parent::match($request, $pathOffset);
}
}
I found the solution but this is NOT elegant solution i think. However everything works perfectly. If somebody knows disadvantages of this solution, please comment this answer or add another, better. I had to modify #mmmshuddup's idea (you can read the conversation).
First of all, the implementation of ServiceLocatorAwareInterface in custom route class is no more necessary.
In Module.php within onBootstrap function:
$app = $e->getApplication();
$sm = $app->getServiceManager();
$sm->get('translator');
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$sm->setFactory('Navigation',
'Zend\Navigation\Service\DefaultNavigationFactory', true);
$nav = $sm->get('Navigation');
$alias = $sm->get('Application\Router\Alias');
$alias->setNavigation($nav);
First we instantiate Navigation factory in ServiceManager and then our custom route. After that we can pass Navigation class into custom route using setNavigation function.
To complete instantiate of our custom route we need in getServiceConfig in the same file:
return array(
'factories' => array(
'Application\Router\Alias' => function($sm) {
$alias = new \Application\Router\Alias('/node[/:id]');
return $alias;
},
'db_adapter' => function($sm) {
$config = $sm->get('Configuration');
$dbAdapter = new \Zend\Db\Adapter\Adapter($config['db']);
return $dbAdapter;
},
)
);
And here is a tricky part. This instance is temporary. While routing, this class will be instantiated one more time and this is why, I think, it's not very elegant. We have to insert parameter into constructor however at this moment value of this parameter is not important.
The custom route class:
// file within ModuleName/src/ModuleName/Router/Alias.php
namespace Application\Router;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Mvc\Router\Http;
class Alias extends Http\Segment
{
private static $_navigation = null;
public function match(Request $request, $pathOffset = null)
{
//some logic here
//get Navigation
$nav = self::$_navigation;
return parent::match($request, $pathOffset);
}
public function setNavigation($navigation){
self::$_navigation = $navigation;
}
}
Because first instance is temporary, we have to collect our Navigation class in static variable. It's awful but works nice. Maybe there is a way to instantiate it only once and in route configuration get instance of it, but at this moment this is best answer for my question. Simply enough and working correctly.

Categories