I'm using cakePhp to create a Rest api (see http://book.cakephp.org/2.0/fr/development/rest.html) and I need to get nested resources. The documentation tells how to get let's say books implementing a URI /books.json. But does not tell how to get for example reviews for a given book. What I'm trying to make is somthing like this: /books/14/reviews.json that returns Review resources.
Can any one tell me hwo to make this?
See the Custom REST Routing section of the docs you've linked. In case the default routing doesn't work for you, you'll have to create your own custom routes that either replace or extend the default ones.
Your /books/14/reviews.json URL could for example be mapped to BooksController::reviews() likes this:
Router::connect(
'/books/:id/reviews',
array(
'[method]' => 'GET',
'controller' => 'books',
'action' => 'reviews'
),
array(
'id' => Router::ID . '|' . Router::UUID,
'pass' => array(
'id'
)
)
);
When placed before Router::mapResources() it should work fine together with the default routes.
Related
I'm trying to config a RESTful API resources mapping in CakePHP3. I followed some tuts but can't make it works.
https://book.cakephp.org/3.0/en/development/routing.html#creating-restful-routes
http://www.bravo-kernel.com/2015/04/how-to-prefix-route-a-cakephp-3-rest-api/
I'm using 'prefix' route to map /api/v2/:resource with my sub-folders inside Controllers folder.
My file structure:
And this is my routing config
Router::prefix('api/v2', function ($routes) {
$routes->resources('Users');
// $routes->get('users', ['controller' => 'Users', 'action' => 'view']);
// $routes->post('users', ['controller' => 'Users', 'action' => 'create']);
// $routes->post('token', ['controller' => 'Users', 'action' => 'token']);
// $routes->fallbacks(DashedRoute::class);
});
As I read in the tuts i mention before, this should works, but i get a Missing Contoller Exception for ApiController.
If I uncomment the last line, enabling fallbacks it works fine, but it's not matching the Controller Methods with the HTTP method GET, POST, DELETE, PUT as CakePHP3 documentation mention.
Any ideas? My Cake version in 3.5.8
Thanks!!
EDIT: using bin/cake routes it's seems like the routes are fine. I'm using Postman to make request with differents HTTP methods to test this.
EDIT 2: I tried with another prefix Foo to avoid the V2 case sensitive issue, and well, this is extrage, the routes seems to be fine, but cake are not matching any of them..
Your folder name should be V2 not v2.
Suppose we do have an Apigility URL like localhost:8888/user.
But I do have two API's in a single Apigility project, ApiOne and ApiTwo.
Doing a GET request from localhost:8888/user always returns resources generated by ApiTwo. But when I want to get the same user resource rom ApiOne because it is from a different DB I can't retrieve it. I already tried to supply the Accept media type with the proper version (hoping it would help), to access ApiOne (e.g. Accept application/vnd.apione.v1+json)
I'm guessing based on your question that you've got ApiOne with a route to /user and ApiTwo with a route to /user?
The problem is that they are not different routes. ZF2 is going to use (I believe) the one that is defined last in the combined configuration file. If you want to be able to use both API resources, you'll need to have different routes somehow. This could be literally changing one of them to something like /user1.
You could also theoretically route to two different endpoints for entities if you provide constraints on the id that would make them differentiable. What I mean is you could have both routes like this:
/api/user[/:user_id]
/api/user[/:user_name]
As far as the ZF2 router is concerned, at this point, those routes are identical. The variable parts user_id and user_name could be the same thing.
However, you'd need something else that makes the routes different. Apigility (and ZF2) allow for constraints on the parts of the URL. So you could put a constraint on user_id of [0-9]+ and a constraint on user_name of [a-z]+. These would make the routes mutually exclusive for entities, but the collection version of the routes is still the same.
Constraints are added in the route under options like so:
'router' => array(
'routes' => array(
'your-api.rest.user' => array(
'type' => 'Segment',
'options' => array(
'route' => '/user[/:user_id]',
'defaults' => array(
'controller' => 'YourApi\\V1\\Rest\\User\\Controller',
),
'constraints' => array(
**'user_id' => '[0-9]+'**,
),
),
),
'your-api.rest.username' => array(
'type' => 'Segment',
'options' => array(
'route' => '/user[/:user_name]',
'defaults' => array(
'controller' => 'YourApi\\V1\\Rest\\Username\\Controller',
),
'constraints' => array(
**'user_name' => '[a-z]+'**,
),
),
),
),
),
)
In this example, if no user_id or user_name were provided, the Username resource should be called since it is defined last.
Overall though, my recommendation would be that you change the routes so that they are different. The accept header with version is essentially used for versioning, not routing to a different API resource.
My accurate question would be, is there any routes that can make possible that when i go
mydomain.com/profilename
it'd be redirect to the profile controllers instead of index, and sitll, if i provide no parameter, he'd load the index page, and even still, if theres a controller by that name, that he'd run that controller instead of searching for a profile...
Pretty much complicated i know, that's why i'm asking for your help, you geniouses! <3
Thanks in advance, Jorge.
ZF1 doesn't have route priority as such, but routes are matched LIFO (last in, first out). So as long as you were able to hard code controller names into your routes, and put this after your profile route, you could do something like this:
$router->addRoute('profile',
new Zend_Controller_Router_Route('/:profilename', array(
'module' => 'default',
'controller' => 'profile',
'action' => 'view'
))
);
$router->addRoute('something',
new Zend_Controller_Router_Route('/:controller/:action', array(
'module' => 'default',
'action' => 'index'
), array(
'controller' => '(foo|bar)' // names of your controllers
))
);
Alternatively, if this isn't possible, or you want a more robust (but more difficult) solution, I wrote a blog post a while back with a detailed explanation of how to achieve this with a custom route class: http://tfountain.co.uk/blog/2010/9/9/vanity-urls-zend-framework
Was curious if anyone knew the best way to implement the following: I have a parameter in my zend framework 1.12 app which effectively controls the 'scope' of things, and is a field in every table in my db to represent the scope of a row. It is a simple integer variable, and can be thought of as 'buildingID', so it controls which 'building' we are working with.
In a plugin, I have:
Zend_Controller_Front::getInstance()->getRouter()->setGlobalParam('building', DYNAMIC_INT);
which accomplishes what I need. When I build a URL with the URL view-helper I have my parameter, but it is always at the end of the parameter list. I know this is trivial from a programming perspective, but how would I achieve 'prepending' this global param to my url parameters?
site.com/admin/controller/action/param1/xyz/param2/xyz/building/2
to become
site.com/admin/controller/action/building/2/param1/xyz/param2/xyz ?
Open to any ideas. If you want me to overload the url view helper, can you provide some example code, because I had trouble setting up this class.
Thank you all!
You can use a custom route to accomplish this. Setup the route somewhere in your bootstrap file:
$route = new Zend_Controller_Router_Route(
':controller/:action/building/:building/*'
);
$router = Zend_Controller_Front::getInstance()->getRouter();
$router->addRoute('building', $route);
And then, assuming that the following has been called at some point prior to using the url view helper...
Zend_Controller_Front::getInstance()->getRouter()->setGlobalParam('building', DYNAMIC_INT);
...you can specify the route as the second argument of the helper:
echo $this->url(array(
'controller' => 'admin',
'action' => 'controller',
'param1' => 'xyz',
'param2' => 'xyz',
), 'building');
// /admin/controller/building/1/param1/xyz/param2/xyz
I'm building my first Zend Framework application and I want to find out the best way to fetch user parameters from the URL.
I have some controllers which have index, add, edit and delete action methods. The index action can take a page parameter and the edit and delete actions can take an id parameter.
Examples
http://example.com/somecontroller/index/page/1
http://example.com/someController/edit/id/1
http://example.com/otherController/delete/id/1
Until now I fetched these parameters in the action methods as so:
class somecontroller extends Zend_Controller_Action
{
public function indexAction()
{
$page = $this->getRequest->getParam('page');
}
}
However, a colleague told me of a more elegant solution using Zend_Controller_Router_Rewrite as follows:
$router = Zend_Controller_Front::getInstance()->getRouter();
$route = new Zend_Controller_Router_Route(
'somecontroller/index/:page',
array(
'controller' => 'somecontroller',
'action' => 'index'
),
array(
'page' => '\d+'
)
);
$router->addRoute($route);
This would mean that for every controller I would need to add at least three routes:
one for the "index" action with a :page parameter
one for the "edit" action with an :id parameter
one for the "delete" action with an :id parameter
See the code below as an example. These are the routes for only 3 basic action methods of one controller, imagine having 10 or more controllers... I can't imagine this to be the best solution. The only benefit that i see is that the parameter keys are named and can therefore be omitted from the URL (somecontroller/index/page/1 becomes somecontroller/index/1)
// Route for somecontroller::indexAction()
$route = new Zend_Controller_Router_Route(
'somecontroller/index/:page',
array(
'controller' => 'somecontroller',
'action' => 'index'
),
array(
'page' => '\d+'
)
);
$router->addRoute($route);
// Route for somecontroller::editAction()
$route = new Zend_Controller_Router_Route(
'somecontroller/edit/:id',
array(
'controller' => 'somecontroller',
'action' => 'edit'
),
array(
'id' => '\d+'
)
$router->addRoute($route);
// Route for somecontroller::deleteAction()
$route = new Zend_Controller_Router_Route(
'somecontroller/delete/:id',
array(
'controller' => 'somecontroller',
'action' => 'delete'
),
array(
'id' => '\d+'
)
$router->addRoute($route);
I tend to look at it this way:
Determine processing requirements.
What does each "action" need? An edit action and a delete action probably require an :id param. An add action and a list action probably do not. These controllers/actions then consume the params and do the processing.
Note: You can write these comtrollers/actions without any reference to the urls that bring visitors there. The actions simply expect that their params will be delivered to them.
Decide (!) what url's you want.
In general, I find the the (/:module/):controller/:action part of the url largely works fine (except for top-level relatively-static pages like /about, where I often put the actions on an IndexController (or a StaticController) and resent having to include the /index prefix in the url.
So, to handle posts, you might want urls like:
/post - list all posts, probably with some paging
/post/:id - display a specific post
/post/:id/edit - edit a specific post
/post/:id/delete - delete a specific post
/post/add - add a post
Alternatively, you might want:
/post/list - list all posts, probably with some paging
/post/display/:id - display a specific post
/post/edit/:id - edit a specific post
/post/delete/:id - delete a specific post
/post/add - add a post
Or any other url scheme. The point is, you decide the url's you want to expose.
Create routes...
...that map those urls to controllers/actions. [And make sure that whenever you render them, you use the url() view-helper with the route-name, so that a routing change requires no changes to your downstream code in your actions or views.
Do you end up writing more routes this way? Yeah, I find that I do. But, for me, the benefit is that I get to decide on my urls. I'm not stuck with the Zend defaults.
But, as with most things, YMMV.
It all depends on your exact requirements. If you simply want to pass one or two params, the first method will be the easiest. It is not practical to define route for every action. A few scenarios where you would want to define routes would be:
Long urls - If the parameter list for a particular action is very long, you might want to define a route so that you can omit the keys from the request and hence shorten the url.
Fancy urls - If you want to deviate from the normal controller/action url pattern of the Zend Framework, and define a different url pattern for your application (eg, ends with ".html")
Slugs / SEO friendly URLs
To take the example of a blog, you might want to define routes for blog posts urls so that the url is SEO friendly. At the same time, you may want to retain the edit / delete / post comment etc urls to remain the ZF default and use $this->getRequest->getParam() to access the request parameters in that context.
To sum up, an elegant solution will be a combination of routes and the default url patterns.
In a previous answer #janenz00 mentioned "long urls" as one of the reasons for using routes:
Long urls - If the parameter list for a particular action is very long, you might want to define a route so that you can omit the keys from the request and hence shorten the url.
Let's say we have an employee controller with an index action that shows a table of employees with some additional data (such as age, department...) for each employee. The index action can take the following parameters:
a page parameter (required)
a sortby parameter (optional) which takes one column name to sort by (eg age)
a dept parameter (optional) which takes a name of a department and only shows the employees that are working in that department
We add the following route. Notice that when using this route, we cannot specify a dept parameter without specifying a sortby parameter first.
$route = new Zend_Controller_Router_Route(
'employee/index/:page/:sortby/:dept',
array(
'controller' => 'employee',
'action' => 'index')
);
If we would fetch these parameters in our action methods instead, we could avoid this problem (because the parameter keys are specified in the url):
http://example.com/employee/index/page/1/dept/staff
I might be looking at it the wrong way (or might not see the full potential of routing), but to me the only two reasons for using routes are:
If your urls don't conform to the traditional /module/controller/action pattern
If you want to make your urls more SEO-friendly
If your sole reason for using routes is to make use of the named parameters, then I think it's better to fetch these parameters in your action methods because of two reasons:
Keeping the number of routes at a minimum will reduce the amount of time and resources spent by the router
Passing in the parameter keys in the url allows us to make use of more complex urls with optional parameters.
Any thoughts or advice on this topic are more than welcome!