I'm trying to use a hierarchical resource in ZF2 for a Restful API. The resource should looks like clients/1/addresses. What I've tried was this
'clients' => array(
'type' => 'segment',
'options' => array(
'route' => '/clients[/:id]',
'constraints' => array(
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Api\Controller\ClientController',
),
),
'may_terminate' => true,
'child_routes' => array(
'addresses' => array(
'type' => 'segment',
'options' => array(
'route' => '/addresses[/:address_id]',
'constraints' => array(
'address_id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Api\Controller\AddressController',
),
),
),
),
),
There is this conflict of both id's, but I don't know if I rename the route identifier id of the resource addresses like I did will solve it. Anyway, the real problem is that the route clients/1/addresses calls the get method of the AddressController, not the getList, and I think that's because Zend understands that the id of the client belongs to addresses, so its calls the get method.
Do you know how to deal with this?
You are probably right that get is called instead of getList because of the id being present in your route match parameters and the controller by default uses 'id' for matching the route identifier.
The way to deal with this is that you give the route identifiers names that fit the resource. So for client you make client_id and for address you use address_id (like you already did).
And then you configure your AbstractRestfulController instance to "look" for the correct route identifier using the setIdentifierName method:
$clientController->setIdentifierName( 'client_id' );
$addressController->setIdentifierName( 'address_id' );
This is just an example, the best way to do this is (of course) by using a controller factory...
Related
I have a problem with a routing in Zend framework.
'name' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '/site/:id/orders[/:page]',
'constraints' => array(
'id' => '[0-9]*',
'page' => '[0-9]*'
),
'defaults' => array(
'controller' => 'Application\Controller\Site',
'action' => 'action'
),
),
),
And in a controller.
$id = (int) $this->params()->fromRoute('id');
And in some (!) cases a browser returns this error - "Missing parameter 'id'", but I don't know why.
Can anybody help me on this issue?
well based on your route configuration id must exist in your routes, so the link you requested didn't have id . and your constrains also should change to 'id' => '[0-9]+' so the id must exist.
and also you get the id in the controller by just typing
$id=$this->params("id");
which will get the id too
I'm working with zend framework 2 and I need to create an optional parameter for a route segment that also has two required parameters. Below is a snippet from the module.config.php describing the route. My understanding is that in ZF2 an optional route parameter can be created by using the
[/:param]
which you can see is what I have. It works fine as long as I send the optional param, but when I leave it out the first two params "uname and appname" are appended together into the "uname" constraint. The route is a parent route.
'roles' => array(
'type' => 'segment',
'options' => array(
'route' => '/roles/:uname/:appname[/:locnames]',
'constraints' => array(
'uname' => '[a-zA-Z].+',
'appname' => '[a-zA-Z0-9_-].+',
'locnames' => 'locnames'
),
'defaults' => array(
'controller' => 'Roles/Controller/RolesController'
),
),
),
What am I missing here, I know you can have define optional parameters, but I can't figure out the correct format
Thanks to grizzm0 on #zftalk or helping me with this one. It was a simple regular expressions issue. Removing the dot(.) in the constraints correctly matched the incoming url parameters. So my route now looks like this:
'roles' => array(
'type' => 'segment',
'options' => array(
'route' => '/roles[/:action][/uname/:uname][/appname/:appname][/locnames/:locnames]',
'constraints' => array(
'uname' => '[a-zA-Z]+',
'appname' => '[a-zA-Z0-9_-]+',
'locnames' => 'locnames'
),
'defaults' => array(
'controller' => 'Roles/Controller/RolesController'
),
),
),
'roles' => array(
'type' => 'segment',
'options' => array(
'route' => '/roles[/:action][/uname/:uname][/appname/:appname][/locnames/:locnames]',
'constraints' => array(
'uname' => '[a-zA-Z].+',
'appname' => '[a-zA-Z0-9_-].+',
'locnames' => 'locnames'
),
'defaults' => array(
'controller' => 'Roles/Controller/RolesController'
),
),
),
You can configure your route like this way.
Here inside your roles controller
you have some action live index.
so your route will be
siteurl/roles/index/uname/john/appname/stackexchange/locanames/yourlocanames
here if you don't want to write appname then remove youre paramater so your route will work.
I'm trying to create a dynamic route in a ZF2 project. It will be something like "domain.com/companyurl/products". The company url is dynamic. I did it:
'company' => array(
'type' => 'Segment',
'options' => array(
'route' => '[/:company]',
'defaults' => array(
'controller' => 'IndexController',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
...
),
),
But I always have to pass the company parameter in a route.
$this->url('company/products', array('company' => 'companyurl'));
Is there some way to specify a base route at the runtime, like a base url, then all route will follow it? Something like this:
$this->url('products');
or
$this->url('company/products');
In the both cases I already specified the base route value.
I hope you understand what I mean. Thanks.
There is a $reuseRouteParams option that you can use in the URL helper:
$this->url($name, $params, $options,$reuseMatchedParameters);
If you set this to true it will reuse the previous route match value of companyUrl.
You can read more on this in the docs here.
In Zend Framework 2, I want to be able to create a route structure like this:
/list -> literal, will always be "list"
/list/industry -> segment, can be stuff like "private", "non-profit", "governmental "
/list/industry/type -> segment, can be stuff like "plumbing", "medical", "food"…
/list/industry/type/company -> segment, can be stuff like "Joe's plumbig", "Sally's Bakery"…
The route can end after every part, which means every line in the explanation above should represent a valid route.
To solve this, I came up with two possible solutions:
1. Using one literal route (/list) with 4 recursively nested child routes. Stripped down example of what I mean:
'list' => array(
'type' => 'literal',
'options' => array(
'route' => '/list',
'defaults' => array(
'action' => 'list'
),
),
'may_terminate' => true,
'child_routes' => array(
'industry' => array(
'type' => 'segment',
'options' => array(
'route' => '/:industry',
'defaults' => array(
'action' => 'industry'
),
),
'may_terminate' => true,
'child_routes' => array(
'company-type' => array(
'type' => 'segment',
'options' => array(
'route' => '/:type',
'defaults' => array(
'action' => 'industry'
),
),
'may_terminate' => true,
'child_routes' => array(
'company' => array(
'type' => 'segment',
'options' => array(
'route' => '/:company',
'defaults' => array(
'action' => 'industry'
),
),
),
),
),
),
),
),
)
I am really not sure if this is a proper way of doing it. What I would like about this approach is that I could use different actions.
2. Using only one child route, but optional segments:
'list' => array(
'type' => 'literal',
'options' => array(
'route' => '/list'
),
'may_terminate' => true,
'child_routes' => array(
'my-child-route' => array(
'type' => 'segment',
'options' => array(
'route' => '[/:industry[/:type[/:company]]]',
),
),
),
)
While the second idea's config looks much more maintainable, I suspect that the controller/action structure will be messy, because if I am not mistaken, one action needs to handle all segment parts with this structure, so I could either use 'defaults' to specify this action, or do it like this:
'options' => array(
'route' => '[/:action[/:type[/:company]]]',
'constraints' => array(
'artist' => '[industry]+'
),
),
Inside the industryAction(), I then would need to do something like this (pseudo code):
if ($this->params['company']) {
// …
} elseif ($this->params['type']) {
// …
}
In this case, I am not sure how to handle wrong URLs like /list/private/plumbing/sallys-bakery – what if Sally's Bakery exists, but obviously not under "plumbing".
So you see, I have some ideas how such route structure could be build, but I am not sure if they are any good or how to approach this further.
What is the recommended way to build such an route structure in Zend Framework 2?
I don't see anything wrong with your first approach:
The three possible routes point to different data being displayed (in terms of data type)
As you already stated, you can (and should) use different actions, or event controllers (which I'd do), keeping your actions clean of routing logic (if/else)
The controller/action is independent of the route and only focuses on one task (e.g. display company details)
I actually do disagree on the argument that the 2nd is more maintainable. For example you can't add different branches further up the route (e.g. industry/location/company).
While writing, I some questions popped my mind though:
Apparently every company can only be located on exactly one route branch. But what happens if a company is listed in two different industries or types? Are they accessible from the both? This can quickly result on duplicate content on different URIs (which contradicts the Unique Identifier part and is really bad for search engine ranking).
Maybe it might be better to implement this slightly different, with only two routes: one for listing companies (optionally by search criteria, be it as query args or as optional route fragments), and one for listing the company's details. This would result in two very clean controllers, one for listing (incl. filtering), one for details.
This is of course without knowing your requirements and further plans and probably a matter of preference as well.
I am trying to solve the routing problem I am facing right now with no luck (googled for hours, saw dozens of examples and solved questions but none works for me - the closest one is this Routing Zend Framework 2 Language in url but even this is not working for me).
I have created an SSO application (I mean the authentication part) and now I am porting it to ZF2 app (I have it almost working as I workarounded the router but need now the final routing to be done) where only these types of URLs are possible:
http[s]://domain.com/login/asfgsdgdsfgzsdgdsf/
http[s]://domain.com/login/tdhjsdgbndfnfgdnhd/
http[s]://domain.com/logout/asfgsdgdsfgzsdgdsf/
http[s]://domain.com/info/dthnzdfbdfhgnfsd/
Those login, logout or info parts are not the controllers nor actions but the methods names I then call on SSO service from within my IndexController and indexAction(). The rest part of the URL is the client application hash.
Now here is my router config which only matches the home route and when the other parts (method name [and hash]) are provided, I got 404 from the ZF2 app (so at least I am in the app context).
return array(
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '/',
'defaults' => array(
'controller' => 'SSO\Controller\Index',
'action' => 'index',
'act' => 'login', // by doing this I am able to workaround the router and work with SSO
'client' => '<my_secret_hash>', // by doing this I am able to workaround the router and work with SSO
),
),
'child_routes' => array(
'action' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '[/:act]',
'defaults' => array(
'act' => 'login',
),
'constraints' => array(
'act' => '(login|logout|info)?', // only login, logout or info are allowed
),
),
'child_routes' => array(
'client' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '[/:client]',
'constraints' => array(
'client' => '[a-zA-Z0-9]+',
),
'defaults' => array(
'client' => '<my_secret_hash>',
),
'may_terminate' => true,
),
),
),
),
),
),
),
);
(only router part of my module.config is provided...)
Now with that router only http[s]://domain.com[/] is matched and either of http[s]://domain.com/(login|logout|info)[/] or http[s]://domain.com/(login|logout|info)/adfbsDVdfbzdbxfbndzxbxfnb[/] matches into A 404 error occured.
Though I try to define the route parts as optional (just for the testing and development purposes), they should by required in the production environment. Anyway, I tried to define them NOT optional too, but didn't work either.
Question: How should I configure the router to match my routes (URLs) defined in the beginning?
Couple of things:
My guess is that you have too many slashes in there -- a child route of / doesn't need to be defined with a slash at the beginning since it's parent already got it
I don't see a reason for 2 nested routes (of course I don't know the rest of your config, so it's a guess)
home route probably should be Literal and able to terminate (again a guess)
there was one unmatched parenthesis (probably 'misspasted' or something)
I suppose this should work for you:
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Literal',
'may_terminate' => true,
'options' => array(
'route' => '/',
'defaults' => array(
'controller' => 'SSO\Controller\Index',
'action' => 'index',
),
),
'child_routes' => array(
'whateva' => array(
'type' => 'Segment',
'may_terminate' => true,
'options' => array(
'route' => '[:act[/:client]]', // as Sam suggested
'defaults' => array(
'act' => 'login',
'client' => 'fsadfasf32454g43g43543',
),
'constraints' => array(
'act' => '(login|logout|info)',
'client' => '[a-zA-Z0-9]+',
),
),
),
),
),
),
),