ZF2 Have all routes resolve to one controller and action - php

I'm trying to develop and single page app with AngularJS and ZF2. I'm having trouble with the routing. My idea is to have routes like /:controller/:action resolve to a single action. I've read the docs and tried the different routing types.
My idea is to have all /:controller/:action to resolve to App\IndexController\IndexAction so only the Angular init code is returned. After the initial page has loaded, Angular will send a secondary request to /template/:controller/:action to retrieve the template and /api/:controller/:action to gather to template's data.
Below is an example of the Regex route I attempted to make work.
'app' => array(
'type' => 'Regex',
'options' => array(
'regex' => '/(?[a-zA-Z][a-zA-Z0-9_-]*\/[a-zA-Z][a-zA-Z0-9_-]*)',
'defaults' => array(
'controller' => 'App\Controller\IndexController',
'action' => 'index',
),
'spec' => '/'
),
'may_terminate' => true,
),

Just use a Segment route:
'app' => array(
'type' => 'Segment',
'options' => array(
'route' => ':controller-fake/:action-fake',
'defaults' => array(
'controller' => 'App\Controller\Index',
'action' => 'index',
),
),
),

Why you just don't define 3 major routes of segment type. See the code below:
'all' => [
'type' => 'Segment',
'options' => [
'route' => '/app/[:controller[/:action]]',
'constraints' => [
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
],
'defaults' => [
'controller' => 'App\Controller\IndexController',
'action' => 'index'
],
],
],
'template' => [
'type' => 'Segment',
'options' => [
'route' => '/template/[:controller[/:action]]',
'constraints' => [
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
],
'defaults' => [],
],
],
'api' => [
'type' => 'Segment',
'options' => [
'route' => '/api/[:controller[/:action]]',
'constraints' => [
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
],
'defaults' => [],
],
]

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'
]
]
]
]
]
]
],

Route path for different api version in zend framework 3

This is how I defined route for my api. It is prefixed with /api/v1. But now few new modules are added in api v2 and all v1 apis are remains same and available in v2. How can i modify this routes that will serve all routes belongs to /api/v1 and when /api/v1 is called and it should serve both /api/v2 and /api/v1 when /api/v2 is called?
module.config.php
'product' => array(
'type' => 'Zend\Router\Http\Segment',
'options' => array(
'route' => '/api/v1/categories[/:id]',
'defaults' => array(
'controller' => CategoryController::class,
),
),
),
'products' => array(
'type' => 'Zend\Router\Http\Segment',
'options' => array(
'route' => '/api/v1/products[/:id]',
'defaults' => array(
'controller' => ProductsController::class,
),
),
),
// ... at lots of v1 apis
//these are introduced in v2
'trends' => array(
'type' => 'Zend\Router\Http\Segment',
'options' => array(
'route' => '/api/v2/trends[/:id]',
'defaults' => array(
'controller' => TrendsController::class,
),
),
),
You can move those common v1 and v2 to a single parent route and v2-only ones to another. Below is sample (not tested) code that should help you understand the idea.
return [
// in Config.router.routes
'api' => [
'child_routes' => [
'v1' => [
'child_routes' => [
// your API 1-and-2 routes
'product' => [/* … */],
'products' => [/* … */]
],
'may_terminate' => false,
'options' => [
'constraints' => ['version' => 'v1|v2'],
'route' => '/:version'
],
'type' => Segment::class
],
'v2' => [
'child_routes' => [
// your API 2 routes
'trends' => [/* … */]
],
'may_terminate' => false,
'options' => ['route' => '/v2'],
'type' => Literal::class
]
],
'may_terminate' => false,
'options' => ['route' => '/api'],
'type' => Literal::class
]
];
If you prefer to not use child routes, you can simply add a route parameter/constraint instead of /v1:
return [
'product' => [
'options' => [
'constraints' => [
'id' => '…',
'version' => 'v1|v2'
],
'defaults' => ['controller' => CategoryController::class],
'route' => '/api/:version/categories[/:id]'
],
'type' => Segment::class
]
];
I know this is late, but I just found this question.
Whilst #gsc's answer is somewhat ok, this is not the correct answer.
This is the correct answer, and this is how I use it:
'api' => [
/** Our main route is /api **/
'may_terminate' => true,
'options' => ['route' => '/api'],
'type' => Literal::class,
'child_routes' => [
/** Since our main route is /api, this will become /api/v1/YOUR_ACTIONS **/
'v1' => [
'type' => Segment::class,
'options' => [
'route' => '/v1[/:action]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
],
'defaults' => [
'controller' => Controller\ApiV1Controller::class,
'action' => 'index',
],
],
],
/** Since our main route is /api, this will become /api/v2/YOUR_ACTIONS **/
'v2' => [
'type' => Segment::class,
'options' => [
'route' => '/v2[/:action]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
],
'defaults' => [
'controller' => Controller\ApiV2Controller::class,
'action' => 'index',
],
],
],
/** Add as many "versions" as you want, all with different controllers. **/
],
],
This allows you to use different "versions" of your controller and is shorter, better understandable and complies to standards.
Enjoy!

ZendFramework2 routing between modules

I have a SkeletonApplication installed and implemented some controllers into the standard module 'Application'.
That works fine.
But now I want to use a second module and I want to set a route from within the 'Application'-module to the new module for linking it there in a view.
The Second module is named 'Sporttabs'.
In my application.config.php I've set as found in documentation:
// This should be an array of module namespaces used in the application.
'modules' => array(
'Application',
'Sporttabs'
),
In the module 'Application' I set in module.config.php:
'routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/',
'defaults' => array(
'module' => 'Application',
'controller' => 'Index',
'action' => 'index',
),
),
),
'fach' => array(
'type' => 'segment',
'options' => array(
'route' => '/index[/:action][/:id]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Index',
'action' => 'index'
),
),
),
'sporttabs' => array(
'type' => 'segment',
'options' => array(
'route' => '/sporttabs[/:controller][/:action][/:id]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'module' => 'Sporttabs',
'controller' => 'Sporttab',
'action' => 'index'
),
),
),
),
),
I tried linking it within index.phtml:
Sporttabs-Projekt
This doesn't work, I only get /sporttab
Even if I try to do www.myurl.de/sporttabs I don't get into the Sporttabs-Module...
(I'm using ZendStudio to generate the ne Module, so I think all file are in right position...)
Can you give me a hint how to do this ?
There is no need to define sporttabs inside your Application config. I suggest you do this inside your Sporttabs' module.config.php file.
This is an example of my /admin route, which is a different module named Admin, which sit next to Application.
'router' => [
'routes' => [
'admin' => [
'type' => 'Literal',
'options' => [
'route' => '/admin',
'defaults' => [
'__NAMESPACE__' => 'Admin\Controller',
'controller' => 'Index',
'action' => 'index',
],
],
'may_terminate' => true,
'child_routes' => [
'default' => [
'type' => 'Segment',
'options' => [
'route' => '/[:controller[/][:action[/][:id][/page/:page][/search/:search]]]',
'constraints' => [
'controller' => '[a-zA-Z0-9_-]*',
'action' => '[a-zA-Z0-9_-]*',
'search' => '[a-zA-Z0-9_-]*',
'id' => '[0-9]+',
'page' => '[0-9]+',
],
'defaults' => [
'__NAMESPACE__' => 'Admin\Controller',
'controller' => 'Index',
'action' => 'index',
],
],
],
],
],
],
],
From there i do this:
<?=$this->url("admin/default", ['controller' => "controler_name", "action" => "action_name_from_controller", "id" => id_or_something_else_if_needed])?>
/default is there in order to have access to child routes.
#Stanimir hits the right hint for my solution:
While editing the routing for my application, I must have made a unintentional change in the order of the routes array:
The 'may_terminate' and 'child_routes' section moved to a upper level instead of being part of the 'fach'-route.
So I changed the array as follows:
'routes'=> array(
'fach'=> array(
'type' => 'Literal',
'options' => array(
'route' => '/',
'defaults' => array(
'__NAMESPACE__' => 'Application\Controller',
'controller' => 'Index',
'action' => 'index',
),
),
'may_terminate' => 'true',
'child_routes' => array(
'default' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:controller[/][:action[/][:id]]]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'__NAMESPACE__' => 'Application\Controller',
'controller' => 'Index',
'action' => 'index',
),
),
),
),
),),
As an aftereffect of including the Namespace in the routing, I also had to change the controllers-array, because the alias name of the controller changed from 'Index' to 'Application\Controller\Index':
'controllers' => array(
'invokables' => array(
'Application\Controller\Index' => 'Application\Controller\IndexController',
),
),
The same error blocked me from getting into the second module, the routes-array was misordered too.
Now the link-solution Stanimir posted in his answer works fine an I get into my new module...
Thanks for your help Stanimir!

How do I route with variable actions and keys?

I am working in Zend Framework 2 and have variable actions and primary keys. Below is my modules router. I would like to pass different ID's to it based on the action (If I delete the language it will reference a languageId if I delete an album it will reference an albumId). Do I need to create child routes for language and album and then identify the specific ID used for each child route? Or is there a way where I can say the second parameter will always be a number and the action will do what it needs to with that number?
Action: deleteLanguage ID: languageId
ACtion: deleteAlbum ID: albumId
'router' => array(
'routes' => array(
'pro' => array(
'type' => 'segment',
'options' => array(
'route' => '/pro[/][:action][/:id]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'language_id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Pro\Controller\Pro',
'action' => 'index',
),
Many Thanks,
M
You only can create a dependency between the action and the identifier if you write down the configuration completely. ZF2 has no method to rename parameter names based on other parameter values (and if you think about it, there are plenty good reasons not to have this feature).
You are left with two options:
'pro' => [
'type' => 'literal',
'options' => [
'route' => '/pro',
'defaults' => [
'controller' => 'Pro\Controller\Pro',
'action' => 'index',
],
'may_terminate' => true,
'child_routes' => [
'delete-language' => [
'type' => 'segment',
'options' => [
'route' => '/delete-language/:language_id',
'defaults' => [
'action' => 'deleteLanguage'
],
'constraints' => [
'language_id' => '[0-9]+'
],
],
],
'delete-album' => [
'type' => 'segment',
'options' => [
'route' => '/delete-album/:album_id',
'defaults' => [
'action' => 'deleteAlbum'
],
'constraints' => [
'album_id' => '[0-9]+'
],
],
],
],
],
]
Or accept you deal with an "id" only (which is much more preferred imho):
'pro' => [
'type' => 'segment',
'options' => [
'route' => '/pro[/:action[/:id]]',
'defaults' => [
'controller' => 'Pro\Controller\Pro',
'action' => 'index',
],
'constraints' => [
'id' => '[0-9]+'
],
],
]
Also, it helps a lot if you split your controllers per entity / domain model. Don't put a delete for a "language" and a delete for an "album" in the same controller. Split them! Think more in RESTful principles and it will keep your code much cleaner.

ZF2 Admin routing

Hi i'm trying to set two modules to work togheter. One is responsible for the front pages (Front module) and the other for the admin pages (Admin module).
I have configured the routing of the two modules normally and everything is working fine when i test them separatly. Once I try to make them work together I have some routing problems.
For example when i try to go to the link : localhost.com/en/admin/index/add the framework uses the front module routing configuration and not the admin.
So basically it tries to go to:
Module - "Front",
Lang - "en",
Controller - "Admin",
Action - "Index"
But what I need is :
Module - "Admin",
Lang - "en",
Controller - "Index",
Action - "add"
Param - "none"
So the need is to tell to the front configuration routing not to access the links that have the admin part in them but I can't get it to work. Here is my front module routing:
'application' => array(
'type' => 'Segment',
'options' => array(
'route' => '[/:lang][/:controller[/:action[/:id]]]',
'constraints' => array(
//'controller' => '^((?!admin).)*[a-zA-Z][a-zA-Z0-9_-]*',
'lang' => '[a-zA-Z]{2}',
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'__NAMESPACE__' => 'Application\Controller',
//'lang' => 'en',
'controller' => 'Index',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'default' => array(
'type' => 'Segment',
'options' => array(
'route' => '[/:lang][/:controller[/:action[/:id]]]',
'constraints' => array(
'lang' => '[a-zA-Z]{2}',
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
),
),
),
),
),
and the admin routing code :
'admin' => array(
'type' => 'Segment',
'options' => array(
'route' => '/admin[/:lang][/:controller[/:action[/:id]]]',
'constraints' => array(
//'controller' => '^((?!admin).)*[a-zA-Z][a-zA-Z0-9_-]*',
'lang' => '[a-zA-Z]{2}',
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'__NAMESPACE__' => 'Admin\Controller',
'lang' => 'en',
'controller' => 'Index',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'default' => array(
'type' => 'Segment',
'options' => array(
'route' => '[/:lang][/:controller[/:action[/:id]]]',
'constraints' => array(
'lang' => '[a-zA-Z]{2}',
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
),
),
),
),
),

Categories