Problem with plugin routes and multilanguage in a Cakephp4 - plugins

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;
}

Related

Lumen POST Routes not Working

Consider the following:
$router->group([
'prefix' => 'api/v1/group',
'middleware' => 'auth'
], function () use ($router) {
$router->get('/', [
'as' => 'group.list',
'uses' => 'Api\V1\GroupController#list'
]);
$router->post('/', [
'as' => 'group.create',
'uses' =>'Api\V1\GroupController#create'
]);
$router->get('/{groupUUID}', [
'as' => 'group.retrieve',
'uses' =>'Api\V1\GroupController#retrieve'
]);
$router->put('/{groupUUID}', [
'as' => 'group.update',
'uses' => 'Api\V1\GroupController#update'
]);
});
As you can see, a pretty typical route setup. However, I'm seeing some incredibly odd behaviour - in short, the POST route seems to be being interpreted by the app as a GET route. When I make a POST request to api/v1/group (via Postman) I don't see the result of Api\V1\GroupController#create, but the result of Api\V1\GroupController#list.
I wondered if perhaps this was something to do with both routes having the same endpoint (shouldn't matter, but maybe it's different in Lumen? I usually work in full-on Laravel). So I commented out the get route. That made me just see a 404.
I then wondered if perhaps this entire route group was somehow broken. So I made two catchall endpoints:
$router->get('/{any:.*}', function () use ($router) {
return 'I am a get route';
});
$router->post('/{any:.*}', function () use ($router) {
return 'I am a post route';
});
And placed them at the top of the routes file, and commented out all other routes. Regardless of the route I hit or the method used, I always saw the same thing: I am a get route.
What's going on? What could cause my app to understand all POST requests as GET requests?
PS: It's also worth noting that these routes were working, until recently, without any real associated changes. Could something have been updated in a Lumen package that caused this?
PPS: I also tried using Insomnia instead of Postman, just in case it was a problem with Postman. Same result.
$router->get('/api/item/{table}/{id}', "ItemController#itemHandler");
$router->post('/api/item/{table}', "ItemController#itemHandler");
$router->put('/api/item/{table}/{id}', "ItemController#itemHandler");
$router->delete('/api/item/{table}/{id}', "ItemController#itemHandler");
I had pretty much the same issue. In my case - since I use Laravel Valet as development environment - I was able to make a POST request again, after serving the API locally over HTTP by executing valet unsecure my-project. On my production server, I still can use HTTPS, but for my local development environment, this solved the issue. Hope this helps some future readers.
Try api/v1/group/ (with trailing slash).

CakePHP3 mapping RESTFul routes

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.

Simple page app routes to same view or controller SailsJS

How can I route multiple urls to the same controller or view to work with angular single page app?!
I can do this but i think is ugly..
'/': {
view: 'homepage'
},
'/login': {
view: 'homepage'
},
'/register': {
view: 'homepage'
},
'/troller': {
view: 'homepage'
},
............
I want somethink like
['/','/login','/register','/troller'] -> view: 'homepage'
And other question, Can I use regular expressions for routing?
Thx!! and sorry for my english.
You can't currently use real regular expressions for routing. You can however use a wildcard route to do what you want (aim multiple routes at one view).
Put this at the end of your /config/routes.js object:
'/*': function(req, res, next) {
if (req.path.match(/\..*/g)) {
return next();
} else {
return res.view('homepage');
}
}
and everything that isn't matched by one of the routes above it will execute that function. The function first checks if you're trying to access a static asset (something with a file extension, like .js or .css), and if so, continues matching the route so that the Express static middleware can server the file. Otherwise, it will server your view.
Update
As of Sails v0.10.0-rc5, regular expressions can be used to define routes. From the docs:
The syntax for a regular expression route is:
"r|<regular expression string>|<comma-delimited list of param names>"
That's the letter "r", followed by a pipe, a regular expression string without delimiters, another pipe, and a list of parameter names that should be mapped to parenthesized groups in the regular expression. For example:
"r|^/\d+/(\w+)/(\w+)$|foo,bar": "MessageController.myaction"
will match /123/abc/def, running the myaction action of MessageController and supplying the values abc and def as req.param('foo') and req.param('bar'), respectively.
You can also route them simultaneously using | operator
'/' | '/login' | '/register' | '/troller': {
view: 'homepage'
}
I don't know if your approach succeded but in my head mapping angular routes clientside to node routes serverside is misleading. My approach to an single page application with these two libraries would be:
having the default sails view deliver your index and angular.js
having the angular routes working your clientside routes together with controllers, etc
managing the complete ui and page flow in angular
using the default sails api's with angular ressource to work on the node models (dont forget to use promises)
using the node / sails routes to add specials on top of that if needed (e.g. login management)
using css framework to compile css (e.g. sass with compass or less)

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.

Zend-Route for ajax API

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