Routing with named parameters - cakephp

I have a URL that contains named parameters, which I want to map to a more user friendly URL.
Take, for example, the following URL:
/videos/index/sort:published/direction:desc
I want to map this to a more friendly URL, like:
/videos/recent
I have tried setting it up in the Router, but it doesn't work.
Code samples from the Router:
Router::connect(
'/videos/recent/*',
array('controller' => 'videos', 'action' => 'index'),
array('sort' => 'published', 'direction' => 'desc'
));
Which doesn't work. And the following also doesn't work:
Router::connect(
'/videos/recent/*',
array('controller' => 'videos', 'action' => 'index', 'sort' => 'published', 'direction' => 'desc'));
Any ideas?

Use get args
The easiest way to have routes work is to avoid named arguments all together. With pagination thats easy to achieve using appropriate config:
class FoosController extends AppController {
public $components = array(
'Paginator' => array(
'paramType' => 'querystring'
)
);
}
In this way when you load /videos/recent you should find it includes urls of the form:
/videos/recent?page=2
/videos/recent?page=3
Instead of (due to route mismatching)
/videos/index/sort:published/direction:desc/page:2
/videos/index/sort:published/direction:desc/page:3
But if you really want to use named args
You'll need to update your route definition - there's no page in the route config:
Router::connect(
'/videos/recent/*',
array(
'controller' => 'videos',
'action' => 'index',
'sort' => 'published',
'direction' => 'desc'
)
);
As such, if there is a page named parameter (which there will be for all urls generated by the paginator helper), the route won't match. You should be able to fix that by adding page to the route definition:
Router::connect(
'/videos/recent/*',
array(
'controller' => 'videos',
'action' => 'index',
'sort' => 'published',
'direction' => 'desc',
'page' => 1
)
);
Though even if it works, you may find it to be fragile.

lets see on [Router::connect documentation](Routes are a way of connecting request urls to objects in your application)
Routes are a way of connecting request urls to objects in your application
So, it's map urls to objects and not url to url.
You have 2 options:
use Router::redirect
Something like that:
Router::redirect( '/videos/recent/*', '/videos/index/sort:published/direction:desc');
but seems that's not that you want exactly
use Router::connect
use normal Router::connect which will connect url to some action which make appropriate scope. Something like that:
Router::connect(
'/videos/recent/*',
array(
'controller' => 'videos',
'action' => 'recent'
)
);
and in VideosController
public function recent() {
$this->request->named['sort'] = 'published';
$this->request->named['direction'] = 'desc';
$this->index();
}
it works and I saw such usage, but not sure, that is will satisfy you too.
as for me, I like normal named cakephp parameters. If such scope (published and desc) is your default state, just code default state in index action. For over cases i think it's normal to use ordinary named parameters.

Related

CakePHP 3 use default routing in some cases

I have a CakePHP 3.5 website. It was necessary to set slugs without any url prefixes like /pages/slug, so I wrote the following rule:
$routes->connect(
'/:slug',
['controller' => 'Pages', 'action' => 'redirectPage']
)
->setMethods(['GET', 'POST'])
->setPass(['slug'])
->setPatterns([
'slug' => '[a-z0-9\-_]+'
]);
It works nice, but in some cases I want cakePHP to route as default (Controller/Action/Params). For example, I want /admin/login to call 'login' action in 'AdminController'.
I have two ideas that doesn't need exact routings, but I can't make any of them to work:
Filtering some strings by pattern: It would be nice if I could filter some strings, that if slug doesn't match pattern, it will simply skip the routing rule.
Create a '/admin/:action' routing rule, but then I cant use :action as an action variable. It causes errors.
$routes->connect(
'/admin/:action',
['controller' => 'Admin', 'action' => ':action']
)
Any ideas?
Thanks
You can use prefix for admin restricted area.
Example:
Router::prefix('admin', function ($routes) {
$routes->connect('/', ['controller' => 'Users', 'action' => 'login']);
$routes->fallbacks(DashedRoute::class);
});
$routes->connect('/:slug' , [
'controller' => 'Pages',
'action' => 'display',
'plugin' => NULL
], [
'slug' => '[A-Za-z0-9/-]+',
'pass' => ['slug']
]);
Now, for example, path /admin/dashboard/index will execute method in Admin "subnamespace" \App\Controller\Admin\DashboardController::index()
Its nicely described in docs: https://book.cakephp.org/3.0/en/development/routing.html#prefix-routing
Try this:
$routes->connect(
'/admin/:action',
['controller' => 'Admin'],
['action' => '(login|otherAllowedAction|someOtherAllowedAction)']
);
Also, your slug routes seems to not catch /admin/:action routes, b/c dash is not allowed there: [a-z0-9\-_]+

How to redefine the URLs generation without changes at the HtmlHelper#link(...) calls in CakePHP?

I have a CakePHP website with many internal links, that are build with the HtmlHelper:
/app/View/MyController/myaction.ctp
<?php
echo $this->Html->link(
$item['Search']['name'],
array(
'controller' => 'targetcontroller',
'action' => 'targetaction',
$profileId,
$languageId
)
);
?>
It works fine with the default route:
/app/Config/routes.php
Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
The generated links look like that: /mycontroller/myaction/$profileId/$languageId.
Now I want to use search engine friendly URLs (with profile names and ISO-639-1 language codes instead of IDs) for a part of the website and added a new Route:
/app/Config/routes.php
Router::connect(
'/:iso6391/:name.html',
array('controller' => 'mycontroller', 'action' => 'myaction'),
array(
'iso6391' => '[a-zA-Z]+',
'name' => '[0-9a-zA-ZäöüßÄÖÜ\-]+',
)
);
And it also works fine and the incomming URIs like /producer/en/TestName.html are interpreted correctly.
But the HtmlHelper is still generating the old URIs like /mycontroller/myaction/1/1.
The docu says:
Reverse routing is a feature in CakePHP that is used to allow you to easily change your URL structure without having to modify all your code. By using routing arrays to define your URLs, you can later configure routes and the generated URLs will automatically update.
Well, the HtmlHelper gets a routing array as input, that means: I'm using the reverse routing.
Why does it not work? How to make the HtmlHelper generate the new URLs (without changing the HtmlHelper#link(...) calls)?
Bit of explanation first
You are technically not using reverse routing. You see, the output link, /mycontroller/myaction/1/1 definitively doesn't match /iso/name.html. Like, in no way. So, the routing skips that rule because it doesn't apply.
Code
Try this
echo $this->Html->link(
$item['Search']['name'],
array(
'controller' => 'targetcontroller',
'action' => 'targetaction',
'iso6391' => $someStringWithIso,
'name' => $someName
)
);
But for that, you have to change your routing a bit, because you are not passing the parameters (check the docs for examples)
Router::connect(
'/:iso6391/:name.html',
array('controller' => 'mycontroller', 'action' => 'myaction'),
array(
'pass' => array('iso6391', 'name'),
'iso6391' => '[a-zA-Z]+',
'name' => '[0-9a-zA-ZäöüßÄÖÜ\-]+',
)
);
And you have to mind the first string match /:iso6391/:name.html. Do you want to match this route to every controller and action in your project, or just the one controller and the one view. If it is for all projects, just for precaution, use this
/:controller/:action/:iso6391/:name.html
if is just for, say, Controller1 and action "view", use
/controller1/view/:iso6391/:name.html
The detail you need to consider is the extension you use .html, is that really necessary in the url? If it is, add it as a parameter in the Html#link
echo $this->Html->link(
$item['Search']['name'],
array(
'controller' => 'targetcontroller',
'action' => 'targetaction',
'iso6391' => $someStringWithIso,
'name' => $someName
'ext' => 'html'
)
);
and also add parseExtensions to the routing file. Read this. Would be easier if you don't add the extension, but that's up to you.
In the end, you still have to change your calls to Html->link...

want to remove action name from url CakePHP

i am working on a Cakephp 2.x.. i want to remove the action or controller name from url ... for example i am facing a problem is like that
i have a function name index on my Messages controller in which all the mobile numbers are displaying
the url is
www.myweb.com/Messages
now in my controller there is a second function whose name is messages in which i am getting the messages against the mobile number
so now my url becomes after clicking the number is
www.myweb.com/Messages/messages/823214
now i want to remove the action name messages because it looks weired...
want to have a url like this
www.myweb.com/Messages/823214
When connecting routes using Route elements you may want to have routed elements be passed arguments instead. By using the 3rd argument of Router::connect() you can define which route elements should also be made available as passed arguments:
// SomeController.php
public function messages($phoneNumber = null) {
// some code here...
}
// routes.php
Router::connect(
'/messages/:id', // E.g. /messages/number
array('controller' => 'messages', 'action' => 'messages'),
array(
// order matters since this will simply map ":id"
'id' => '[0-9]+'
)
);
and you can also refer link above given by me, hope it will work for you.
let me know if i can help you more.
REST Routing
The example in the question looks similar to REST routing, a built in feature which would map:
GET /recipes/123 RecipesController::view(123)
To enable rest routing just use Router::mapResources('controllername');
Individual route
If you want only to write a route for the one case in the question
it's necessary to use a star route:
Router::connect('/messages/*',
array(
'controller' => 'messages',
'action' => 'messages'
)
);
Usage:
echo Router::url(array(
'controller' => 'messages',
'action' => 'messages',
823214
));
// /messages/823214
This has drawbacks because it's not possible with this kind of route to validate what comes after /messages/. To avoid that requires using route parameters.
Router::connect('/messages/:id',
array(
'controller' => 'messages',
'action' => 'messages'
),
array(
'id' => '\d+',
)
);
Usage:
echo Router::url(array(
'controller' => 'messages',
'action' => 'messages',
'id' => 823214 // <- different usage
));
// /messages/823214
in config/routes.php
$routes->connect('/NAME-YOU-WANT/:id',
['controller' => 'CONTROLLER-NAME','action'=>'ACTIOn-NAME'])->setPass(['id'])->setPatterns(['id' => '[0-9]+']
);
You can use Cake-PHP's Routing Features. Check out this page.

cakephp named parameters break REST based web service

I have a json REST based in the form of:
Router::mapResources('Test');
Which is equivalent to for the index method to:
Router::connect( '/Test',
array(
'controller' => 'ChannelSources',
'action' => 'index',
'[method]' => 'GET' ),
array();
I am trying to add support for named parameters this method.
but apparently its breaks the Router method as an index action is not part of the URL
i have tried using
Router::connectNamed(array('somenameparam'));
But it failed.
I would create a specific route so that you can pass in the right parameters, then you can pass your params into the route.
Have a look at, http://book.cakephp.org/2.0/en/development/routing.html#passing-parameters-to-action
Router::connect(
'/blog/:id-:slug', // E.g. /blog/3-CakePHP_Rocks
array('controller' => 'blog', 'action' => 'view'),
array(
// order matters since this will simply map ":id" to $articleId in your action
'pass' => array('id', 'slug'),
'id' => '[0-9]+'
)
);

CakePHP 2.0 Router::connect issue without visible id in url

I want following SEO url like:
www.example.com/users/profile/webfacer
I do not want to use the unique user to fetch from database.
I try to use the Router method connect in my AppController. but I realised that it isn't possible (or not knowing it right now to use it in their also used in routes.php does not helped) like this:
//in AppController
Router::connect('/users/profile/:name',
array(
'controller' => 'users',
'action' => 'profile'
) ,
array(
'pass' => array('id', 'name'),
'id' => '[0-9]+'
)
);
How can I reproduce this link (below the example) with this html link helper to send the id but not show it in the url:
$this->Html->link('webfacer',array(
'controller'=>'users',
'action'=>'profile',
'id'=>1,
'name'=>'webfacer'
));
This would output www.example.com/users/profile/username:webfacer that mean my router doesn't appear to my route options.
Has anybody had the same issues and solved this?
Because you haven't put the :id argument in your route string, Cake won't know what to do when you pass it in the helper, that is why it's just appending it as a normal param in the URL. There is no way to pass a "hidden" id with the URL, you're best bet is to either expose it or at the other end of the app write something that fetches the ID based on the username you pass (make sure this column is indexed and url-safe).
I would just simplify your route to this:
//in AppController
Router::connect('/users/profile/:name',
array(
'controller' => 'users',
'action' => 'profile'
) ,
array('pass' => array('name'),
)
);
And don't bother passing ID to the helper. In your profile action you'd just have something like this:
public function profile($name) {
$user = $this->User->find('first', array('conditions' => array('name' => $name)));
}

Resources