Reputation: 2720
In Zend Framework 3, is it possible to route to a controller depending on whether a URL contains a query string?
For example, I have these two URLs:
/users
/users?name=Bob
I would like the first route to call a UsersController
and second route to call a NameController
.
Is this possible?
Upvotes: 3
Views: 1149
Reputation: 3468
Have a read of the ZF3 docs on Router and possibly RFC 3986 Chapter 3 - Syntax Components which shows what is path and what is query.
From RFC 3986 Chapter 3 - Syntax Components
The following are two example URIs and their component parts:
foo://example.com:8042/over/there?name=ferret#nose \_/ \______________/\_________/ \_________/ \__/ | | | | | scheme authority path query fragment | _____________________|__ / \ / \ urn:example:animal:ferret:nose
ZF3 route configuration is typically configuration on a path. (This is also true of pretty much every common framework.) Yes, variables can be part of a path. As such, they're configured in route configuration. Advanced configuration of framework routing often also allows for changes/requirements in schemes and authorities.
Not part of routing configurations are the 'query' and 'fragment' parts.
If you want to do something, e.g. catch a "name" key/value pair and do routing accordingly, you're going to have to create a "catcher" (or whatever the name for that would be) on the path and determine the redirection yourself.
For example, you could do something like this answer. If your controller instance extends the default Zend Framework AbstractActionController
class, then you should have the forward
plugin available. From the docs:
Forward returns the results of dispatching the requested controller; it is up to the developer to determine what, if anything, to do with those results. One recommendation is to aggregate them in any return value from the invoking controller.
As an example:
$foo = $this->forward()->dispatch('foo', ['action' => 'process']);
return [
'somekey' => $somevalue,
'foo' => $foo,
];
Of course, you could immediately return it.
Another option is the redirect
plugin (same link).
return $this->redirect()->toRoute('login-success');
With all this you can do something like:
$name = $this->params()->fromQuery('name', null);
if ($name) {
// dispatch
if ($dispatchResult) {
// return special
}
}
// redirect
Where you redirect to a route name (ie. configured path)
Upvotes: 4
Reputation: 1308
Even if rkeet's answer is technically correct, I'd avoid that solution because it transform a single HTTP request into two (the initial GET and the forward/redirect).
I'd configure the route paths in order to have two different routes:
'router' => [
'routes' => [
'users' => [
'type' => Literal::class,
'options' => [
'route' => '/users',
'defaults' => [
'controller' => UsersController::class,
'action' => 'index'
],
'may_terminate' => true,
'child_routes' => [
'name' => [ // This is the subroute name
'type' => Segment::class,
'options' => [
'route' => '/:name', // Option 1
// 'route' => '/name/:name', // Option 2
'defaults' => [
'controller' => NameController::class,
'action' => 'index'
],
'constraints' => [
'name' => '[a-zA-Z]+',
]
]
]
]
]
]
]
]
With this configuration, you can access the following URL's
/users
/users/Bob (option 1)
/users/name/Bob (option 2)
Upvotes: -1
Reputation: 2720
As rkeet mentioned, query string is not part of the route. Therefore, we simply added a ternary to the route config array:
'users' => [
'type' => Literal::class,
'options' => [
'route' => '/users',
'defaults' => [
'controller' => isset($_GET['name'])
? NameController::class
: UsersController::class,
'action' => 'index',
],
],
'may_terminate' => true,
],
It's self explanatory, but basically the controller that is dispatched is dependent on whether a value is set in the query string.
We decided not to use the forward()
plugin because we do not want to instantiate an extra controller superfluously.
Upvotes: -1