We're using ZF2 for an web-application and want to test it with phpunit (v4.8.9). Within this application we've got a scheme-route, to be able to switch between http/https-context (Doesnt matter why...). The route looks like this:
'http' => array(
'type' => 'Scheme',
'options' => array(
'scheme' => 'http',
'defaults' => array(
'http' => true
)
),
'child_routes' => array(
'search' => array(
'type' => 'segment',
'options' => array(
'route' => '/search[/:keyword[/:page]]',
'constraints' => array(
'page' => '[1-9]+[0-9]*'
),
'defaults' => array(
'controller' => SearchController::class,
'action' => 'search',
),
),
),
),
),
'https' => array(
'type' => 'Scheme',
'options' => array(
'scheme' => 'https',
'defaults' => array(
'https' => true
)
),
'child_routes' => array(
'search' => array(
'type' => 'segment',
'options' => array(
'route' => '/search[/:keyword[/:page]]',
'constraints' => array(
'page' => '[1-9]+[0-9]*'
),
'defaults' => array(
'controller' => SearchController::class,
'action' => 'search',
),
),
),
),
),
The class of the test looks like this:
class SearchControllerTest extends SynHttpControllerTestCase
{
public function setUp()
{
$this->setApplicationConfig($this->getCurrentBootstrap()->getApplicationConfig());
parent::setUp();
$this->getApplicationServiceLocator()->setAllowOverride(true);
}
public function testSearchActionCanBeAccessed()
{
$this->dispatch('/search');
$this->assertResponseStatusCode(200);
$this->assertControllerName(SearchController::class);
$this->assertControllerClass('SearchController');
$this->assertActionName('search');
$this->assertMatchedRouteName('search');
}
}
FYI:
The "SynHttpControllerTestCase" is an extension from the original AbstractHttpControllerTestCase which comes with Zend-Test. It's modified to get the right bootstrap-file in our tests.
If we're running the tests, this error appears:
Fatal error: Call to a member function getParam() on null in C:\xampp\htdocs\git\xxx\vendor\zendframework\zend-test\src\PHPUnit\Controller\AbstractControllerTestCase.php on line 563
We looked into the AbstractControllerTestCase and this line is throwing the error:
$routeMatch = $this->getApplication()->getMvcEvent()->getRouteMatch();
Because the $routeMatch-Object is empty.
We've some other controllers and tests within our application, they're all fine and not affected from this problem, cause the routes to these controllers arent scheme-routes.
Do you have any ideas how to solve this? In advance: we're not able to use static https-routes in this case.
There is a lot of official documentation on how to write a proper controller test. In your setUp method you need to attach a Router instance to a RouteMatch instance which you attach to a MvcEvent in the controller.
protected function setUp()
{
$serviceManager = Bootstrap::getServiceManager();
$this->controller = new IndexController();
$this->request = new Request();
$this->routeMatch = new RouteMatch(array('controller' => 'index'));
$this->event = new MvcEvent();
$config = $serviceManager->get('Config');
$routerConfig = isset($config['router']) ? $config['router'] : array();
$router = HttpRouter::factory($routerConfig);
$this->event->setRouter($router);
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
$this->controller->setServiceLocator($serviceManager);
}
Then you can call dispatch.
UPDATE
Can you not set your route match object like this:
$routeParams = array('controller' => 'search', 'action' => 'search');
$routeMatch = new RouteMatch($routeParams);
$routerConfig = isset($config['router']) ? $config['router'] : array();
$router = HttpRouter::factory($routerConfig);
$event = new MvcEvent();
$event->setRouter($router);
$event->setRouteMatch($routeMatch);
$this->getApplication()->setMvcEvent($event);
Related
I'm thinking about the best way of implementing ACL. So - I need to protect certain routes. Some routes would be accessible only to users, some to guests and some to admins.
It seems like the best way of doing it would be by adding a $role variable in the routing config. So then I'd attach to post-route event, fetch the routeMatch and I would see if this user can enter this route.
How can I do that? Can I simply inject extra varibles like this:
'router' => array(
'routes' => array(
'route1' => array(
'type' => 'Zend\Mvc\Router\Http\Regex',
'options' => array(
'regex' => '/some/route/1',
'defaults' => array(
'controller' => 'Subscriber\Controller\View',
'action' => 'route1',
'role' => 'user', //extra
),
'spec' => '/some/route/1',
),
),
'route2' => array(
'type' => 'Zend\Mvc\Router\Http\Regex',
'options' => array(
'regex' => '/some/route/2',
'defaults' => array(
'controller' => 'Subscriber\Controller\View',
'action' => 'route2',
'role' => 'guest', //extra
),
'spec' => '/some/route/2',
),
),
//other routes....
),
),
Yes you can just add the router key like you have
'defaults' => array(
'controller' => 'Subscriber\Controller\View',
'action' => 'route1',
'role' => 'user', //extra
),
And then you can checkit like this
public function onBootstrap(MvcEvent $e) {
$application = $e->getApplication();
$eventManager = $application->getEventManager();
$eventManager->attach(MvcEvent::EVENT_ROUTE, function(MvcEvent $e) {
$e->getRouteMatch()->getParam('role');
});
}
There are however modules made for this
For example bjyoungblood/BjyAuthorize which works with ZfcUser
You should take a look at : ZfcRbac. It's well documented.
I'm trying to make a cron app in my zf2 project but it gave me always the following message :
Reason for failure: Invalid arguments or no arguments provided
My code is this:
module.config.php
'controllers' => array(
'invokables' => array(
'Sync\Controller\Cron' => 'Sync\Controller\CronController',
'Sync\Controller\Index' => 'Sync\Controller\IndexController'
),
),
'console' => array(
'router' => array(
'routes' => array(
'user-reset-password' => array(
'options' => array(
'route' => 'user resetpassword [--verbose|-v] <userEmail>',
'defaults' => array(
'controller' => 'Sync\Controller\Index',
'action' => 'password'
)
)
),
'cron' => array(
'options' => array(
'route' => 'cron [full|center]',
'defaults' => array(
'controller' => 'Sync\Controller\Cron',
'action' => 'full'
)
)
)
)
)
)
CronController.php
class CronController extends AbstractActionController
{
public function fullAction()
{
$request = $this->getRequest();
if (!$request instanceof ConsoleRequest) {
throw new \RuntimeException('You can only use this action from a console!');
}
return("hi");
}
public function centerAction()
{
}
}
Given that a matching php binary is in the path you can then call from your application folder:
php public/index.php cron full
or just
php public/index.php cron (since the route defaults to the fullAction)
I have one question, about ZF2 Translator, in the specific case in the costruction the link for change langauge dunamically when user click on flag or menu link.
In My Application/config/module.config.php i have this code:
'service_manager' => array(
'factories' => array(
'translator' => 'Zend\I18n\Translator\TranslatorServiceFactory',
'navigation' => 'Zend\Navigation\Service\DefaultNavigationFactory',
),
),
and my route is:
return array(
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:lang[/:action]]',
'constraints' => array(
'lang' => '[a-zA-Z]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'index',
),
),
),
And in my Application/Module.php i set this code on bootstrap:
public function onBootstrap(MvcEvent $e)
{
$sm = $e->getApplication()->getServiceManager();
$router = $sm->get('router');
$request = $sm->get('request');
$matchedRoute = $router->match($request);
$params = $matchedRoute->getParams();
if(isset($params['lang']) && $params['lang'] !== '') {
$translator = $e->getApplication()->getServiceManager()->get('translator');
//or
//$translator = $e->getApplication()->getServiceManager()->get('MvcTranslator');
if($params['lang'] == 'en') {
$translator->setLocale('en_US');
}
}
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
}
now my route http://www.xxxx.com/it/index or http://www.xxxx.com/it/company but i can't create a link in my view for change language in my application...
How do i proceed ?
Thanks
You can create a link to the same page you are in by changing only the lang parameter like the following:
$this->url(null, array('lang' => $anotherLang), array(), true)
Just do this in your Views :
English
This will give the same URL as the current one but in different language (here it's English).
I have problem with the routing in ZF2. I want to make dynamic routing for the software, that I'm making.
For example:
This is the URL: http://localhost:8080/application/index.json/
And this is my module.config (router part):
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
),
),
),
'restful' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '/:module/[:controller[/:action][.:formatter][/:id]]',
'constraints' => array(
'module' => '[a-zA-Z][a-zA-Z0-9_-]*',
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'formatter' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[a-zA-Z0-9_-]*'
),
),
),
),
),
Everything is working fine, but when I create new controller, have to add it to controllers['invokables'] setting in module.config.
'controllers' => array(
'invokables' => array(
'index' => 'Application\Controller\IndexController',
'cloud' => 'Application\Controller\CloudController',
),
),
So the question is, how to automate the controllers['invokables'] to process requests dynamically, without describing every controller in it.
Fast and dirty, but you get the idea.
namespace Application;
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$eventManager->attach (MvcEvent::EVENT_ROUTE, function (MvcEvent $e) {
$controller_loader = $e->getApplication ()->getServiceManager ()->get ('ControllerLoader');
$controller = $e->getRouteMatch ()->getParam ('controller');
$controller_class = '\Application\Controller\\'.ucfirst ($controller).'Controller';
// Add service locator to the controller
$controller_object = new $controller_class;
$controller_object->setServiceLocator ($e->getApplication ()->getServiceManager ());
// ------------------------------------
$controller_loader->setService ($controller, $controller_object);
});
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}
I have the following routing definition:
'admin_default' => array(
'type' => 'segment',
'options' => array(
'route' => '[/:lang]/administrator[/:module][/:action]',
'constraints' => array(
'lang' => '[a-zA-Z]{2}',
'module' => '[a-zA-Z0-9_-]*',
'action' => '[a-zA-Z0-9_-]*',
),
'defaults' => array(
'module' => 'Application',
'controller' => 'Admin',
'action' => 'index',
'lang' => 'ru'
),
),
'may_terminate' => true,
'child_routes' => array(
'wildcard' => array(
'type' => 'wildcard',
'may_terminate' => true,
'options' => array(
'key_value_delimiter' => '/',
'param_delimiter' => '/'
),
),
),
),
So, I can't get rid of segment [/:lang] in URL string
For example:
URL view helper $this->url('admin_default', array('module' => 'albums')) returns the following URL string:
/administrator/albums
while $this->url('admin_default/wildcard', array('module' => 'albums', 'action' => 'edit', 'id' => album_id_here)) returns:
/ru/administrator/albums/edit/id/album_id_here
How can I remove [/:lang] segment from URL string in second case?
so whats the matter with that "ru" ?
you have to extend the zend view helper URL to inject your current locale to the URL
look what i made for my current project :
<?php
namespace PatrickCore\View\Helper;
use Doctrine\ORM\EntityManager;
use Zend\View\Helper\Url;
class I18nUrl extends Url {
/**
* #var String
*/
protected $lang;
protected $router;
public function __construct($locale,$router) {
$arraylanguagemapping = array(
'en_US' => 'en',
'fa_IR' => 'fa'
);
$this->lang = $arraylanguagemapping[$locale];
$this->router = $router;
}
public function __invoke($name = null, array $params = array(), $options = array(), $reuseMatchedParams = false) {
$this->setRouter($this->router);
if (!array_key_exists('lang', $params)) {
$params['lang'] = $this->lang;
}
return parent::__invoke($name,$params,$options,$reuseMatchedParams);
}
}
?>