I want my url route to have a dynamic part and to end up on the same page not matter if whatever I have in the middle part of my URL.
E.g.:
/en/the-old-category/the-old-name/pid/123123123
/en/the-new-category/now-in-a-sub-category/the-new-name/pid/123123123
Should both get caught by the same controller/action (which will issue the pertinent 301/302 if necessary).
My current router contains:
'router' => [
'routes' => [
'blog' => [
'type' => 'segment',
'options' => [
'route' => "/[:language]",
'constraints' => [
'language' => '[a-z]{2}'
],
'defaults' => [
'controller' => 'Blog\Controller\List',
'action' => 'index',
'language' => 'en'
],
'may_terminate' => true,
'child_routes' => [
'detail' => 'segment',
'options' => [
'route' => '/:path/pid/:postid',
'constraints' => [
'path' => '.+',
'postid' => '\d{1,10}'
],
'defaults' => [
'controller' => 'Blog\Controller\List',
'action' => 'detail'
]
]
]
]
]
]
]
But it's not working.
/ and /en are being caught properly, but subroutes like the ones I proposed earlier, are not.
Am I in the right path to do what I want to do? Should I write a regex route instead?
.+ won't match / because segment routes split the path on / before applying constraints. To make your route work you'll need something like /:path/:foo/pid/:postid. A regex route might also work.
Since between the first and the last part of my URL I need to have a variable number of segments, I can't do this with a segment route, but I was able to manage it with a regex route, configured as follows:
'router' => [
'routes' => [
'blog' => [
'type' => 'regex',
'options' => [
'regex' => "/(?<language>[a-z]{2})?",
'spec' => "/%language%",
'defaults' => [
'controller' => 'Blog\Controller\List',
'action' => 'index'
],
'may_terminate' => true,
],
'child_routes' => [
'detail' => [
'type' => 'regex',
'options' => [
'regex' => '/(?<path>.+)/pid/(?<postid>\d+)$',
'spec' => '/%path%/pid/%postid%',
'defaults' => [
'controller' => 'Blog\Controller\List',
'action' => 'detail'
]
]
]
]
]
]
]
Related
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'
]
]
]
]
]
]
],
I'm using ZF3, in module.config.php file in Post module, I have one of these two routes,
'create-post' => [
'type' => Literal::class,
'options' => [
// Change this to something specific to your module
'route' => '/post/create',
'defaults' => [
'controller' => Controller\PostController::class,
'action' => 'create',
]
],
'may_terminate' => true,
'child_routes' => [
// You can place additional routes that match under the
// route defined above here.
],
],
'post' => [
'type' => Segment::class,
'options' => [
// Change this to something specific to your module
'route' => '/post[/:postId]',
'defaults' => [
'controller' => Controller\PostController::class,
'action' => 'show',
],
'constraints' => array(
'postId' => '\d{4}'
)
],
'may_terminate' => true,
'child_routes' => [
// You can place additional routes that match under the
// route defined above here.
],
]
Now when I visit http://localhost:8080/post/create it works, but when I visit http://localhost:8080/post/32, it doesn't work. It say 404 error, page not found.
Any help is much appreciated.
As per #jon Stirling comment on my question, I changed the constraints on post route and it worked.
Changed 'postId' => '\d{4}' to 'postId' => '\d{1,4}'
'post' => [
'type' => Segment::class,
'options' => [
// Change this to something specific to your module
'route' => '/post[/:postId]',
'defaults' => [
'controller' => Controller\PostController::class,
'action' => 'show',
],
'constraints' => array(
'postId' => '\d{1,4}'
)
],
'may_terminate' => true,
'child_routes' => [
// You can place additional routes that match under the
// route defined above here.
],
]
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())
In ZF3 I want to get default parameter from route. I'm getting parameters in this way in controller:
$params = $this->params()->fromRoute('crud');
My urls looks like this:
1: somedomain/admin/color/add
2: somedomain/admin/color
In 1) I'm getting add in my $params variable.
In 2) I'm getting null but I'm expecting default (in this case view)
I think this is problem with bad router configuration.
'admin' => [
'type' => Segment::class,
'options' => [
'route' => '/admin/:action',
'defaults' => [
'controller' => Controller\AdminController::class,
'action' => 'index',
],
],
'may_terminate' => true,
'child_routes' => [
'color' => [
'type' => Segment::class,
'options' => [
'route' => '/:crud',
'constraints' => [
'crud' => 'add|edit|delete|view',
],
'defaults' => [
'controller' => Controller\AdminController::class,
'crud' => 'view',
],
],
],
],
],
In your route definition, you didn't says the router that your crud parameter is optionnal. So when you call somedomain/admin/color, it is the route /admin/:action which is selected.
To specify a optional parameter, use the bracket notation (assuming you use the same action):
'admin' => [
'type' => Segment::class,
'options' => [
'route' => '/admin/:action[/:crud]',
'defaults' => [
'controller' => Controller\AdminController::class,
'action' => 'index',
'crud' => 'view',
],
'constraints' => [
'crud' => 'add|edit|delete|view',
],
],
],
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.