Zend-Route for ajax API - zend-framework

I'm trying to add a route to my application, so that I can use it with ajax calls.
Here is what I have in my application.ini
;Routes
resources.router.routes.products.route = "/backend/api/:command"
resources.router.routes.products.defaults.module = "backend"
resources.router.routes.products.defaults.controller = "api"
resources.router.routes.products.defaults.action = "index"
When a ajax call is made, to /backend/api/SomeCommand, the following error is produced:
Message: Invalid controller specified (backend)
array (
'controller' => 'backend',
'action' => 'maestro',
'module' => 'default',
)
as you can see module has been set to "default", instead of "backend", and controller is "backend" instead of "api", what could have caused this?

Looks like you've got another more generic route defined after this one that's matching the request.
You need to define your routes in order of least to most specific, specificity usually being improved by the presence of fixed terms like your backend/api prefix.
See Basic Rewrite Router Operation, in particular
Note: Reverse Matching
Routes are matched in reverse order so make sure your most generic routes are defined first.
FYI: You don't need to prefix your routes with a forward-slash

Related

Problem with plugin routes and multilanguage in a Cakephp4

I've built a multilanguage NewsManager plugin that has a NewsController
I'm try to write routes in this plugin to be able to access to routes like /en/news-manager/news/, /en/news-manager/news/my-lastest-news, ...
Here is my code :
// in /plugins/NewsManager/config/routes.php
$routes->scope('/{lang}', function (RouteBuilder $routes) {
$routes->plugin('NewsManager', function (RouteBuilder $routes) {
$routes->connect('/news', ['controller' => 'News', 'action' => 'index'])
->setPatterns([
'lang' => 'en|fr'
])
->setPersist(['lang']);
$routes->connect('/news/{slug}', ['controller' => 'News', 'action' => 'view'])
->setPass(['slug'])
->setPatterns([
'lang' => 'en|fr'
])
->setPersist(['lang']);
});
});
When I try to access to /en/news-manager/news/ I have a Missing Controller error with the message NewsManagerController could not be found.
What am I doing wrong ?
(Note that when I write exactly the same code that the code above but in my App routes it works...)
For parsing requests, routes are being ordered by their template's static path portion, which is the porition of the template before the first routing element.
So in your case the static portion would be / for all the language routes, as they immediately start with the {lang} routing element. This will result in a first comes first served order, meaning since your fallback routes are being connected before the plugin routes, they will be processed and matched first, making your plugin routes inaccessible.
As mentioned in the comments, one solution would obviously be to remove the fallback routes. This isn't an uncommon thing to do, as bailing out early at routes lookup will avoid unneccsary processing until the code runs into a problem at controller lookup later on.
Another way would be to connect the fallback routes after the plugin routes. Plugin route loading will always be invokved after application routes loading, so the pretty much only way to achieve this, is to move connecting the respective application routes into the plugin routes loading mechanism, which has a slight workaround smell.
Basically, move those routes into Application::pluginRoutes() to after invoking the parent method:
// in src/Application.php
public function pluginRoutes(\Cake\Routing\RouteBuilder $routes): \Cake\Routing\RouteBuilder
{
// connect plugin routes
$routes = parent::pluginRoutes($routes);
// connect fallback routes
$routes->scope('/{lang}', function (\Cake\Routing\RouteBuilder $routes) {
$routes->fallbacks();
});
return $routes;
}

Deep Wildcard Sailsjs

How do I define deep wildcards in sailsjs?
For example, I can create a wildcard in mysite.com/slug/wildcard by adding /slug/:wildcard/ to my route.
However, this doesn't work: mysite.com/slug/wildcard/extended/. I want to be able to capture all the routes that start with slug.
Is that possible?
I think this is possible with the current revision of sails, as route parameters are defined using :param .. each instance of :something indicates a new param for the route handler. These are not called wildcards but parameters.
I know express supports * for a wildcard, have you tried something like this...
'/slug/*': {
controller: 'Index',
action: 'index'
},

Use route prefix with RESTful routes in CakePHP

Working on building an API and would like to use RESTful routes.
I got it to work just fine like this:
http://www.mysite.com/events.json // returns json results with my events
http://www.mysite.com/events/123.json // returns json results with event of id '123'
BUT - I want to be able to do this using an 'api' prefix.
So, I added the api Routing prefix:
Configure::write('Routing.prefixes', array('admin', 'api'));
And changed my actions from 'view' and 'index' to 'api_view' and 'api_index'.
But now it doesn't work. (eg. I have to write the action name or it won't find the correct one based on HTTP.
The end goal would be to be able to do something like this:
GET http://www.mysite.com/api/1.0/events.json // loads events/api_index()
GET http://www.mysite.com/api/1.0/events/123.json // loads events/api_view($id)
DELETE http://www.mysite.com/api/1.0/events/123.json // loads events/api_delete($id)
...etc
I ended up having to just write the routes manually:
Router::parseExtensions('json', 'xml');
Router::connect('/api/:version/:controller/:id/*',
array('[method]'=>'GET', 'prefix'=>'api', 'action'=>'view'),
array('version'=>'[0-9]+\.[0-9]+', 'id'=>'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'));
Router::connect('/api/:version/:controller/*',
array('[method]'=>'GET', 'prefix'=>'api', 'action'=>'index'),
array('version'=>'[0-9]+\.[0-9]+'));
Router::connect('/api/*', array('controller'=>'events', 'action'=>'index', 'ext'=>'html'));
Notes:
The [method] is what forces the HTTP type (eg. RESTful)
The parseExtensions() makes it so you can have it display the data in different formats automatically by changing the extension in your URL.
The last Router:: line was just a catchall for anything /api/ that didn't match - it forwarded it to the homepage. Eventually I'll probably just route this to an API error page.
The 'ext'=>'html' of the last Router:: line was to keep parseExtensions from trying to use whatever extension was in the URL - if it's redirecting for reasons they made the call wrong, I just want it to go back to the homepage (or whatever) and use the normal view.
Try something like this.
Router::connect('/:api/:apiVersion/:controller/:action/*',
array(),
array(
'api' => 'api',
'apiVersion' => '1.0|1.1|'
)
);
With prefix routing
Router::connect('/:prefix/:apiVersion/:controller/:action/*',
array(),
array(
'prefix' => 'api',
'apiVersion' => '1.0|1.1|'
)
);
Will match only valid API versions like 1.0 and 1.1 here. If you want something else use a regex there.
I know this is an old post, but there is a routing method called mapResources which creates the special method based routing for you.
http://book.cakephp.org/2.0/en/development/rest.html
You put it in routes.php like so:
Router::mapResources(array('controller1', 'controller2'));
The docs have a nice little table showing how the requests are mapped to different actions, which you can always override if you need to.

Why Zend_View_Helper_Url returns relative path when using the default route and absolute path when using a custom route?

I have an application running on Zend Framework 1.12 and I am using the URL view helper. I also have a few custom routes which I use with the URL helper to clean the code.
When I use a format such as this:
$this->url(array('module' => 'myapp', 'controller' => 'index', 'action' => 'index'), 'default', true)
The resulting string is a relative path to the action - /myapp/index/index
But when I use a custom route as such:
$this->url(array('var' => 'value'), 'custom-route', true)
The resulting string is an absolute path - http://www.domain.com/custom-route
I don't understand why is this happening and I can't find any information about this behavior online. Furthermore, I wish to know if there's a way to prevent it from happening and produce consistency in the way the URL view helper works (preferably to always return a relative path).
Thanks.
I think this is as simple as your first example being a default type Zend_Controller_Router_Route_Module route that is normally relative.
Your second example is a named route so it needs to include the base url in order to be valid. I don't believe rewritten routes can be relative as the route may or may not refer to an actual location.
I've done some limited testing and I believe that if you set your baseUrl value the first example will also include the host address. So for consistency your urls will always be absolute.
//application.ini
resources.frontController.baseurl = http://www.domain.com
Hope this helps.

Fetching Zend Framework URL parameters

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!