ZF3: Controllers with child routes doesn't work - php

I'm ZF2 developer and I'm migrating to ZF3 and I'm having troubles whith some controllers.
For example, I have this url: http://localhost/admin which calls to the correct controller (IndexController) and show the correct view. But If I want to associate this url: http://localhos/admin/articulo with ArticuloController doesn't work. When I call to this url: http://localhost/admin/articulo the controller called is AdminController and doesn't find the view.
OPTION 1 => module.config.php:
namespace Admin;
use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
use Zend\ServiceManager\Factory\InvokableFactory;
return [
'router' => [
'routes' => [
'admin' => [
'type' => Segment::class,
'options' => [
'route' => '/admin[/:action]',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
'admin/articulos' => [
'type' => Segment::class,
'options' => [
'route' => '/admin/articulos[/:action]',
'defaults' => [
'controller' => Controller\ArticulosController::class,
'action' => 'index',
],
],
],
],
],
'controllers' => [
'factories' => [
Controller\IndexController::class => InvokableFactory::class,
Controller\ArticulosController::class => InvokableFactory::class,
],
],
'view_manager' => [
'display_not_found_reason' => true,
'display_exceptions' => true,
'doctype' => 'HTML5',
'not_found_template' => 'error/404',
'exception_template' => 'error/index',
'template_map' => [
'layout/layout' => __DIR__ . '/../view/layout/layout-admin.phtml',
'admin/index/index' => __DIR__ . '/../view/admin/index/index.phtml',
'error/404' => __DIR__ . '/../view/error/404.phtml',
'error/index' => __DIR__ . '/../view/error/index.phtml',
],
'template_path_stack' => [
__DIR__ . '/../view',
],
/*
* Con este array de parámetros permitimos enviar datos y no mostrar vista
*/
'strategies' => [
'ViewJsonStrategy',
],
],
];
OPTION 2 => module.config.php (ZF2 style):
namespace Admin;
use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
use Zend\ServiceManager\Factory\InvokableFactory;
return [
'router' => [
'routes' => [
'admin' => [
'type' => Segment::class,
'options' => [
'route' => '/admin[/:action]',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
'admin/articulos' => [
'type' => Literal::class,
'options' => [
'route' => '/admin/articulos[/:action]',
'defaults' => [
'controller' => 'Articulos',
'action' => 'index',
],
],
'may_terminate' => true,
'child_routes' => [
'default' =>[
'type' => Segment::class,
'options' => [
'route' => '/[:controller[/:action][/:id1]]',
'constraints' => [
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id1' => '[0-9_-]*'
],
'defaults' => [],
],
],
],
],
],
],
'controllers' => [
'factories' => [
Controller\IndexController::class => InvokableFactory::class,
Controller\ArticulosController::class => InvokableFactory::class,
],
],
'view_manager' => [
'display_not_found_reason' => true,
'display_exceptions' => true,
'doctype' => 'HTML5',
'not_found_template' => 'error/404',
'exception_template' => 'error/index',
'template_map' => [
'layout/layout' => __DIR__ . '/../view/layout/layout-admin.phtml',
'admin/index/index' => __DIR__ . '/../view/admin/index/index.phtml',
'error/404' => __DIR__ . '/../view/error/404.phtml',
'error/index' => __DIR__ . '/../view/error/index.phtml',
],
'template_path_stack' => [
__DIR__ . '/../view',
],
/*
* Con este array de parámetros permitimos enviar datos y no mostrar vista
*/
'strategies' => [
'ViewJsonStrategy',
],
],
];
OPTION 3 => module.config.php (following zf3 tutorial):
https://docs.zendframework.com/zend-mvc/routing/#http-routing-examples
namespace Admin;
use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
use Zend\ServiceManager\Factory\InvokableFactory;
return [
'router' => [
'routes' => [
'admin' => [
'type' => Segment::class,
'options' => [
'route' => '/admin[/:action]',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
'may_terminate' => true,
'child_routes' => [
'articulos' => [
'type' => Segment::class,
'options' => [
'route' => '/articulos[/:action]',
'defaults' => [
'controller' => Controller\ArticulosController::class,
'action' => 'index'
],
],
],
],
],
],
],
'controllers' => [
'factories' => [
Controller\IndexController::class => InvokableFactory::class,
Controller\ArticulosController::class => InvokableFactory::class,
],
],
'view_manager' => [
'display_not_found_reason' => true,
'display_exceptions' => true,
'doctype' => 'HTML5',
'not_found_template' => 'error/404',
'exception_template' => 'error/index',
'template_map' => [
'layout/layout' => __DIR__ . '/../view/layout/layout-admin.phtml',
'admin/index/index' => __DIR__ . '/../view/admin/index/index.phtml',
'error/404' => __DIR__ . '/../view/error/404.phtml',
'error/index' => __DIR__ . '/../view/error/index.phtml',
],
'template_path_stack' => [
__DIR__ . '/../view',
],
/*
* Con este array de parámetros permitimos enviar datos y no mostrar vista
*/
'strategies' => [
'ViewJsonStrategy',
],
],
];
For all of the configurations when I call the url: http://localhost/admin/articulos the view that I get is ...
Where you can see that the controller called is Admin\Controller\IndexController and not Admin\Controller\ArticulosController
What am I doing wrong?
Update 1:
The option 3 configuration works fine!!! I have delete all the content from /cache directory and now the controller is found but ... I have got now an error rendering the template ...
Message:
Zend\View\Renderer\PhpRenderer::render: Unable to render template
"admin/articulos/index"; resolver could not resolve to a file
Stack Trace:
0 /var/www/html/31juegos/vendor/zendframework/zend-view/src/View.php(207):
Zend\View\Renderer\PhpRenderer->render()
1 /var/www/html/31juegos/vendor/zendframework/zend-view/src/View.php(236):
Zend\View\View->render(Object(Zend\View\Model\ViewModel))
2 /var/www/html/31juegos/vendor/zendframework/zend-view/src/View.php(200):
Zend\View\View->renderChildren(Object(Zend\View\Model\ViewModel))
3 /var/www/html/31juegos/vendor/zendframework/zend-mvc/src/View/Http/DefaultRenderingStrategy.php(105):
Zend\View\View->render(Object(Zend\View\Model\ViewModel))
4 /var/www/html/31juegos/vendor/zendframework/zend-eventmanager/src/EventManager.php(322):
Zend\Mvc\View\Http\DefaultRenderingStrategy->render(Object(Zend\Mvc\MvcEvent))
5 /var/www/html/31juegos/vendor/zendframework/zend-eventmanager/src/EventManager.php(171):
Zend\EventManager\EventManager->triggerListeners(Object(Zend\Mvc\MvcEvent))
6 /var/www/html/31juegos/vendor/zendframework/zend-mvc/src/Application.php(367):
Zend\EventManager\EventManager->triggerEvent(Object(Zend\Mvc\MvcEvent))
7 /var/www/html/31juegos/vendor/zendframework/zend-mvc/src/Application.php(348):
Zend\Mvc\Application->completeRequest(Object(Zend\Mvc\MvcEvent))
8 /var/www/html/31juegos/public/index.php(40): Zend\Mvc\Application->run()
9 {main}

It is a typo issue. Try with this http://localhost/admin/articulos (note the ending "s") because your router is /admin/articulos which points to this ArticulosController's indexAction(). That is why this url http://localhost/admin/articulo (without ending "s") was not able to dispatch. And the view structure should be of type module/controller/action.

(Posted on behalf of the OP).
Finally, I have fixed my last problem. The problem was due to my index.phtml was in an wrong directory /view/admin/articulos/**index/**index.phtml. The correct directory is /view/admin/articulos/index.phtml.

Related

ZF2 Routing Multiple Controllers based on route constraints

I'm refactoring some routing in a project and I'm trying to retain the current path structure of the following...
/events <-- Works
/events/super-bowl <-- Works
/events/2012-super-bowl <-- Doesn't work! Archive Layout
/events/2012-super-bowl/detail-page <-- Doesn't work! Archive sub layout
/events/2018-super-bowl <-- Works. Standard Layout, no sub layout
This is what I have tried...
'router' => [
'routes' => [
'events' => [
'type' => 'literal',
'options' => [
'route' => '/events',
'defaults' => [
'controller' => 'events',
'action' => 'index',
],
],
'may_terminate' => true,
'child_routes' => [
'super-bowl' => [
'type' => 'segment',
'options' => [
'route' => '/super-bowl',
'defaults' => [
'action' => 'superBowl',
],
],
],
'super-bowl-archives' => [
'type' => 'segment',
'options' => [
'route' => '/[:year]-super-bowl[/:detail]',
'constraints' => [
'year' => '^(2012|2013|2014)',
],
'defaults' => [
'controller' => 'super-bowl-archives',
'action' => 'index',
],
],
],
'super-bowl-standard' => [
'type' => 'segment',
'options' => [
'route' => '/[:year]-super-bowl',
'constraints' => [
'year' => '\d{4}'
],
'defaults' => [
'controller' => 'super-bowl-standard',
'action' => 'index',
],
],
],
],
],
],
],
I'm struggling with the archive layouts. I'd like to capture certain years and point them to a different controller. The archives have a sub route as well that I'm not sure how to achieve.
There are two problems in that configuration.
First: matching rule.
Since the matching isn't completely regex based, the correct "pattern" for year is '2012|2013|2014'
Second: matching order.
According to Zend router documentation:
Routes will be queried in a LIFO order, and hence the reason behind the name RouteStack
Last route will be matched first. Since 2012 is matched with \d{4}, you have to first test those "exceptions".
Simply put super-bowl-archives route after super-bowl-standard and it'll work
'router' => [
'routes' => [
'events' => [
'type' => 'literal',
'options' => [
'route' => '/events',
'defaults' => [
'controller' => 'events',
'action' => 'index'
]
],
'may_terminate' => true,
'child_routes' => [
'super-bowl' => [
'type' => 'segment',
'options' => [
'route' => '/super-bowl',
'defaults' => [
'action' => 'superBowl'
]
]
],
'super-bowl-standard' => [
'type' => 'segment',
'options' => [
'route' => '/[:year]-super-bowl',
'constraints' => [
'year' => '\d{4}'
],
'defaults' => [
'controller' => 'super-bowl-standard',
'action' => 'index'
]
]
],
'super-bowl-archives' => [
'type' => 'segment',
'options' => [
'route' => '/[:year]-super-bowl[/:detail]',
'constraints' => [
'year' => '2012|2013|2014'
],
'defaults' => [
'controller' => 'super-bowl-archives',
'action' => 'index'
]
]
]
]
]
]
],

ZF3 How to use optional parameter in homepage route

I'm having trouble adding an optional parameter to my home route.
This is my current router:
'routes' => [
'home' => [
'type' => Segment::class,
'options' => [
'route' => '/[:salon/]',
'constraints' => [
'salon' => '[a-zA-Z][a-zA-Z0-9_-]*'
],
'defaults' => [
'controller' => 'Application\Controller\Index',
'action' => 'index',
'salon' => 'test'
],
],
],
'application' => [
'type' => Segment::class,
'options' => [
'route' => '/application[/:controller[/:action]]',
'constraints' => [
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*'
],
'defaults' => [
'__NAMESPACE__' => 'Application\Controller',
'controller' => 'Application\Controller\Index',
'action' => 'index',
],
],
'may_terminate' => true,
'child_routes' => [
'default' => [
'type' => 'wildcard'
]
]
],
],
My Controller:
<?php
namespace Application\Controller;
class IndexController extends AbstractController
{
public function indexAction()
{
var_dump($this->params('salon'));
die;
}
}
domain.ltd/
This works and I'm getting default value for salon paramter which is 'test'
domain.ltd/test123
Expected value would be 'test123' but this displays me 404 error: The requested URL could not be matched by routing.

Add new controller to existed module in zf3

In Zend framework 3, I tried adding new controller "ArticleController" to a existed module City but failed. I post screen shot, my folder structure and module.config.php. Could you explain what the problem is? Incidentally, it worked when accessing http://0.0.0.0:7000/city
When accessing http://0.0.0.0:7000/article
Next, module\city\config\module.config.php codes are following:
<?php
namespace City;
use Zend\Router\Http\Segment;
return [
'router' => [
'routes' => [
'city' => [
'type' => Segment::class,
'options' => [
'route' => '/city[/:action[/:id]]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
],
'defaults' => [
'controller' => Controller\CityController::class,
'action' => 'index',
],
],
],
'article' => [
'type' => Segment::class,
'options' => [
'route' => '/article[/:action[/:id]]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
],
'defaults' => [
'controller' => Controller\ArticleController::class,
'action' => 'index',
],
],
],
],
],
'view_manager' => [
'template_path_stack' => [
'city' => __DIR__ . '/../view',
],
],
];
Error message is clear. Application doesn't know anything about your controller. Your module config has to have information about controllers under "controllers" key. Checkout zend documentation, you'll see "controllers" key in config file.

ZF3: Action and view doesn't found

In my application I have created a second action in a controller. When I call the application with the url http://local.domain I've got the correct page so it's called the correct controller. But If I want to make this call http://local.domain/liga-futbol-1 it doesn't work and I've got this error:
A 404 error occurred Page not found. The requested URL could not be
matched by routing.
No Exception available
IndexController.php
namespace Stats\Controller;
use Zend\Mvc\Controller\AbstractActionController;
class IndexController extends AbstractActionController {
public function indexAction()
{
//This action serves the page
return [];
}
public function ligaFubtol1Action(){
return [];
} }
module.config.php
namespace Stats;
use Zend\Router\Http\Segment;
use Zend\ServiceManager\Factory\InvokableFactory;
return [
'controllers' => [
'factories' => [
Controller\IndexController::class => InvokableFactory::class,
],
],
'router' => [
'routes' => [
'home' => [
'type' => 'Literal',
'options' => [
// This works!!! => http://local.domain
'route' => '/',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
'may_terminate' => true,
'child_routes' => [
// This doesn't work!!! http://local.domain/liga-futbol-1
'liga-futbol-1' => [
'type' => Segment::class,
'options' => [
'route' => '/liga-futbol-1',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'ligaFutbol1'
],
],
'may_terminate' => true,
'child_routes' => [
],
],
],
],
],
],
'view_manager' => [
'display_not_found_reason' => true,
'display_exceptions' => true,
'doctype' => 'HTML5',
'not_found_template' => 'error/404',
'exception_template' => 'error/index',
'template_map' => [
'layout/layout' => __DIR__ . '/../view/layout/layout.phtml',
'stats/index/index' => __DIR__ . '/../view/stats/index/index.phtml',
'error/404' => __DIR__ . '/../view/error/404.phtml',
'error/index' => __DIR__ . '/../view/error/index.phtml',
],
'template_path_stack' => [
__DIR__ . '/../view',
],
/*
* Con este array de parámetros permitimos enviar datos y no mostrar vista
*/
'strategies' => [
'ViewJsonStrategy',
],
],
];
Directories:
I have checked my cache in "/dir_project/data/cache" and there is nothing.
What am I doing wrong?
Have a look at the route option of the home route: it’s set to /. The liga-futbol-1 route is a child route of the home route, so its URL is a “sum” of:
home URL: /
liga-futbol-1 URL: /liga-futbol-1
In result: //liga-futbol-1 is the URL of the home/liga-futbol-1 route.
If you want just something like /liga-futbol-1, there are two solutions:
Make the liga-futbol-1 route independent of the home route (i.e. not its child):
'routes' => [
'home' => [
'type' => Literal::class,
'options' => [
'route' => '/',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index'
]
]
],
'liga-futbol-1' => [
'type' => Segment::class,
'options' => [
'route' => '/liga-futbol-1',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'ligaFutbol1'
]
]
]
]
Remove / from the beginning of liga-futbol-1’s route option:
'liga-futbol-1' => [
'type' => Segment::class,
'options' => [
'route' => 'liga-futbol-1',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'ligaFutbol1'
]
]
]

ZF3 optional parameter with child routes on Segment class doesn't want to match

I want to create routes with an optional [lang] parameter that would be used to set the application language as follows:
Example with default lang param(EN for example)
domain.tld/
domain.tld/users
domain.tld/contacts
etc.
Example witt new lang param(DE for examle)
domain.tld/de
domain.tld/de/users
domain.tld/de/contacts
etc.
This is my route configuration:
'router' => [
'routes' => [
'site' => [
'type' => Segment::class,
'options' => [
'route' => '[/:lang]',
'constraints' => [
'lang' => '[a-z]{2}',
],
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
'lang' => 'en',
],
],
'may_terminate' => true,
'child_routes' => [
'home' => [
'type' => Literal::class,
'options' => [
'route' => '/',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
'may_terminate' => true,
],
'application' => [
'type' => Segment::class,
'options' => [
'route' => '/application[/:action]',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
'may_terminate' => true,
],
],
],
],
]
When I have lang param in the url everything is fine. But when I try to open "default" lang without specifying it into the path (ex. example.tld/application/test) it gives me 404 error.
What I found is that the final regex after transformation from Segment class is (\G(?:/(?P<param1>([a-z]{2})))?) and path is /application/test. When preg_match is executed on Segment.php:385 it return match with following values:
[
'0' => '/ad',
'param1' => 'ad',
'1' => 'ad',
],
which is obviously the wrong behavior. My language is set to "ad" instead of open application/test action. I tested around 10 more regex but without success... (ex. ^[a-z]{2}$).
What I am doing wrong?
You get 404 because URL does not match site route, so child routes of site are skipped.
Try this:
'home' => [
'type' => Segment::class,
'options' => [
'route' => '/[:lang/]',
'constraints' => [
'lang' => '(en|de)?',
],
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
'lang' => 'en',
],
],
'may_terminate' => true,
'child_routes' => [
'application' => [
'type' => Segment::class,
'options' => [
'route' => 'application[/:action]',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
],
]
If you do not provide lang param then it takes default (en in my example) param.
Example:
example.tld/application/test
lang is en
controller is IndexController
action is test (testAction())
example.tld/de/application/test
lang is de
controller is IndexController
action is test (testAction())

Categories