zf2 routing seems to ignore __NAMESPACE__ - php

In Zend Framework 2, I tried using the following route:
'default' => array(
'type' => 'Segment',
'options' => array(
'route' => '/:username[/:action]',
'defaults' => array(
'__NAMESPACE__' => 'Website\Controller',
'controller' => 'User',
'action' => 'index',
),
),
'may_terminate' => true,
),
However, when going to http://www.example.com/MyUsernameHere, I get a 404 not found error:
The requested controller could not be mapped to an existing controller class.
Controller:
User(resolves to invalid controller class or alias: User)
It's almost like the router completely ignores the 'Website\Controller' namespace and looks for User without the namespace in front it.
So, if I manually enter the namespace like so:
'default' => array(
'type' => 'Segment',
'options' => array(
'route' => '/:username[/:action]',
'defaults' => array(
'controller' => 'Website\Controller\User',
'action' => 'index',
),
),
'may_terminate' => true,
),
then the page loads as expected.
What gives? Can the '__NAMESPACE__' parameter not be used for controllers? The ZF2 website clearly gives an example using '__NAMESPACE__', but I cannot get it work in practice. Is the example wrong and outdated?

For this to work as you expected you have to attach the ModuleRouteListener to the MVC event manager. You can do this in your module onBootstrap method:
public function onBootstrap(MvcEvent $event)
{
//...
$application = $event->getApplication();
$eventManager = $application->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
//...
}
After you did that your code will work as expected.
They actually should have mentioned this in the page with the example that you referred to in your question. You can check for more details on the module route listener here in the Zend\Mvc documentation. They write there:
This listener determines if the module namespace should be prepended to the controller name. This is the case if the route match contains a parameter key matching the MODULE_NAMESPACE constant.

Related

Zendframework - Adding an action to existing controller results in permission denied

I'm using Zendframework (2.3), I'm struggling to understand what I'm doing wrong when attempting to create a new action and view on an existing controller. I've read some relevant documentation but still fail to see what I'm missing.
Currently the controller basically defaults to a single action (main), I would like to add an additional one.
EG:
/list-item/main => // Existing Route
/list-item/add => // New Route I would like to add.
This is how my module.config.php looks:
return array(
'router' => array(
'routes' => array(
'list-item' => array(
'type' => 'segment',
'options' => array(
'route' => '/list-item[/:action][/:id][/:id1][/:id2][/:id3][/:id4][/:id5]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*'
),
'defaults' => array(
'controller' => 'ListItem',
'action' => 'main',
),
),
),
),
),
'controllers' => array(
'invokables' => array(
'ListItem' => 'ListItem\Controller\ListItemController',
),
),
'view_manager' => array(
'template_path_stack' => array(
__DIR__ . '/../view',
),
),
);
If I can read this configuration correctly, the actual action name is optional, but the module will default to the main action. Yet it is flexible to allow for any action to be attempted.
So, I proceeded to add a new public function into the ListItemController, assumed this is how the convention works:
public function addAction() {
return new ViewModel();
}
And with it a new view file into the module's views folder, in fact right next to main.phtml but called add.phtml.
But when I attempt to access the route /list-item/add I only get a permission denied. Funny, because the actual status is 200. But I have no other information. I honestly don't even know if this is a ZF thing, I can only assume.
Also, I'm using php -S 0.0.0.0:8080 -t public/ in case the web server might have something to do.
Thanks in advance, any help will be greatly appreciated.

Segment Routing to factory instead of controller

I am currently setting up a ZF2 application and got stuck with the router. I looked up Zend's example for segmented routing:
$route = Segment::factory(array(
'route' => '/:controller[/:action]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]+',
'action' => '[a-zA-Z][a-zA-Z0-9_-]+',
),
'defaults' => array(
'controller' => 'Application\Controller\IndexController',
'action' => 'index',
),
));
By calling http://example.com/Maps/edit Zend would automatically "navigate" to the MapController and call EditAction().
Since I use Factory for the MapController I am looking for a solution like
$route = Segment::factory(array(
'route' => '/:factory[/:action]',
'constraints' => array(
'factory' => '[a-zA-Z][a-zA-Z0-9_-]+',
'action' => '[a-zA-Z][a-zA-Z0-9_-]+',
),
'defaults' => array(
'factory' => 'Application\Controller\Factory\DefaultControllerFactory',
'action' => 'index',
),
));
Basically I want the framework to access the factory instead of the controller without listing any single factory manually.
Thanks for any suggestions!
controller manager is ServiceManager, all service manager features applies. Register controller factory instead of declaring it as invokable

Zend2: how to add custom route type?

I have a class \Foo\BarRoute implementing the route interface (\Zend\Mvc\Router\RouteInterface).
How do I add \Foo\BarRoute as a bar route plugin and make it available in configuration (e.g. 'type' => 'bar')?
So far I got the following Module.php without any effect :(
public function onBootstrap(EventInterface $e)
{
$routePluginManager = $e->getRouter()->getRoutePluginManager();
$routePluginManager->setInvokableClass('bar', '\Foo\BarRoute');
}
Can this be done via the module configuration file only?
Thanks!
Why not set the FQCN of your custom route class in the module.config.php directly?
in case you just need to use it in your module config file.
e.g.
return array(
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Foo\BarRoute',
'options' => array(
'route' => '/',
'defaults' => array(),
),),
),),
...
);

Zend Framework 2 Console Routes

I'm trying to define some console routes for my ZF2 application as described here http://packages.zendframework.com/docs/latest/manual/en/modules/zend.console.routes.html
in the module config I have:
'console' => array(
'router' => array(
'routes' => array(
'user-set-password' => array(
'options' => array(
'route' => 'user password <username> <password>',
'defaults' => array(
'controller' => 'User\Profile',
'action' => 'setpassword'
),
),
),
),
),
),
but it seems to never match the route as it always prints the usage information. also simple routes like just 'test' won't be matched.
(when I write some crap into the route parameter, the execution fails with an Zend\Mvc\Router\Exception\InvalidArgumentException so it recognizes the console route when loading the module)
is it my fault or maybe a bug in the latest zf2 version?
I just found the solution in an inconsistent interface for the route definitions:
it works if you provide the following schema for the controller:
'controller' => 'User\Controller\Profile'
would be better to be able to define it in the same way as http routes:
'defaults' => array(
'__NAMESPACE__' => 'User\Controller',
'controller' => 'Profile',
'action' => 'setpassword',
),
just opened an issue for that: http://framework.zend.com/issues/browse/ZF2-515

ZF2: How to get Zend\Db\Adapter inside custom router?

As in title, I'm struggling to access DBAdapter inside Router. Implementing ServiceLocatorAwareInterface isn't much help (ZF2 does not inject anything). Declaring it as a service in module with custom factory is not an option either, as it extends Http/Parts router and requires configuration parameters passed depending on a route (I don't want to hard-code them)
What I've already tried:
module.config.php:
(...)
'router' => array(
'routes' => array(
'adm' => array(
'type' => 'Custom\Mvc\Router\Http\Segment',
'options' => array(
'route' => '/admin[/:language[/:controller[/:action[/:params]]]]',
'constraints' => array(
'language' => '(pl|en)',
'controller' => "[a-zA-Z0-9_\-]*",
'action' => "[a-zA-Z0-9_\-]*",
'params' => "(.*)",
),
'defaults' => array( ... ),
),
'may_terminate' => true,
),
),
),
'service_manager' => array(
(...)
'invokables' => array(
'Custom\Mvc\Router\Http\Segment' => 'Custom\Mvc\Router\Http\Segment',
),
),
(...)
As of now, Custom\Mvc\Router\Http\Segment is just a copy of Zend\Mvc\Router\Http\Segment, with added interfaces ServiceLocatorAwareInterface, AdapterAwareInterface and respective methods in the similar fashion:
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
var_dump($serviceLocator);
exit();
}
It never enters the setServiceLocator method, only RouteInterface::factory(), which then calls constructor.
Setting up a factory didn't help either, again - the code is not executed. Same behavior after moving the 'invocables' or factory to application config.
Currently using Zend Framework 2 RC1
It would have been easier if you would have gisted us som code.. :)
My recommendation would either be to use a factory to instansiate your custom router or set it as an invokable class (Requires you to implement ServiceLocatorAwareInterface so you can set it up in the router)

Categories