CakePHP reverse routing not working as expected - cakephp

I need several routes in my application to allow for a dynamic string to proceed the prefix.
Here's my route:
Router::connect('/:location/traveler/:controller/*', array('action' => 'index', 'traveler' => true, 'prefix' => 'traveler'), array('pass' => array('location')));
For instance, if I went to /south/traveler/requests it would route successfully to RequestsController::traveler_index($location = 'south').
This is what I want, but I also need HtmlHelper::link() to properly reverse route a URL array into that route.
Here's my call to HtmlHelper::link():
$this->Html->link('List Requests', array('controller' => 'requests', 'action' => 'index', 'location' => 'south'));
The prefix routing is (or should be) implied since this is being called from a view within the traveler prefix.
The URL that call spits out is:
http://domain.com/traveler/requests/location:south
Have I not done something correctly? Is there any way I can avoid creating a custom route class to properly reverse route these URL arrays?

You need to inform the router that location should be a named parameter using Router::connectNamed. See the Named Parameters section of the CakePHP v1.3 Book:
URL: /contents/view/chapter:models/section:associations
When making custom routes, a common pitfall is that using named parameters will break your custom routes. In order to solve this you should inform the Router about which parameters are intended to be named parameters. Without this knowledge the Router is unable to determine whether named parameters are intended to actually be named parameters or routed parameters, and defaults to assuming you intended them to be routed parameters. To connect named parameters in the router use Router::connectNamed().
Router::connectNamed(array('chapter', 'section'));
Will ensure that your chapter and section parameters reverse route correctly.

I solved the problem.
Removing Router::connectNamed() from routes.php, I repaired my route which was misconfigured.
The reverse route to traveler_index() worked properly using the route I listed above, but any call to any other function, like traveler_edit() would fail.
Using the route below, I was able to get it to reverse route for any action on any controller in the traveler prefix with location as a variable.
Router::connect('/:location/traveler/:controller/:action/*', array('traveler' => true, 'prefix' => 'traveler'), array('pass' => array('location')));
Now, my call to HtmlHelper::link() correctly reverse-routes my URL array:
$this->Html->link('Edit Details', array('controller' => 'requests', 'action' => 'edit', 'location' => 'south', 1234));
...reverse routes to /south/traveler/requests/edit/1234.

Related

how to write route if I want to use query parameters in string and want to direct everything after search* to a function in contoller?

Router::connect('/form/search*', array('controller' => 'Mycontoller', 'action' => 'search'));
I am really confused about this. I have query parameters in my URL for eg. localhost:8888/Search?srchTxt=Val.
I need to route everything from Search to a method in the controller. How can I do that in route.php file in CakePHP? is there another way I can accomplish it?
$routes->connect('/Search', ['controller' => 'Others', 'action' => 'my_action']);
https://book.cakephp.org/3.0/en/development/routing.html#routes-configuration
everything after question mark (?) is accessible thru $this->request->query()
https://book.cakephp.org/3.0/en/controllers/request-response.html#query-string-parameters

Forcing language prefix on URL in CakePHP

I'd like to force my site's URL to always have a language suffix.
So, if they type www.mysite.com it should take them to www.mysite.com/en.
I have a default language, so that should be used if it's their first time to the site. If it's not, I have a Cookie being set that I can use...but - I don't know where to use it.
I thought about checking to see if there was a "language" parameter in the URL, then if not, redirecting, but - that seems overkill - is there a better way? Can I do this in routes? or bootstrap?
The most efficient way would be through your web server. You can easily check if the request is for / (the home page) and redirect to /en.
Check the docs for what ever web server you are using, they all have something like mod_rewrite or similar.
Edit
You could set up a route like /set_default_language to redirect to in case of /, this controller can access the db and do what ever it needs.
Alternatively you can make it redirect to /your/usual/language_switch with no language specified and allow the code to use the default.
What I did:
I ended up checking in the AppController's beforeFilter() whether or not $this->request->params['langauge'] was set and if not, building the URL accordingly:
//Redirect to same url, but with language parameter
if (empty($this->request->params['language']) &&
empty($this->request->params['admin'])) {
$defaultLanguageCode = Configure::read('Languages.default.code2');
$cookiedLanguage = $this->Language->activeLanguageByCode($this->Cookie->read('lang'));
$languageToRedirectTo = (!empty($cookiedLanguage['code2'])) ? cookiedLanguage['code2'] : $defaultLanguageCode;
$newURL = '/' . $languageToRedirectTo . $this->request->here;
$this->redirect($newURL);
}
Note:
The part I couldn't figure out (until getting help in IRC) was to build the URL using $this->request->here, which is just the URL as a string. Prior to that I tried building out the array using the params array, but had no luck.
My routes (in case they help anyone)
(Keep in mind, I'm a routes noob, so - although they seem to be working for me, I do NOT guarantee they're done well!)
//root URL and root URL w/ language
Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home')); // eg: www.google.com takes them to pages/display/home
Router::connect('/:language', array('controller'=>'pages', 'action' => 'display', 'home'), array('language'=>'[a-z]{2}')); // eg: /en takes them to pages/display/home and sets language
//pages (eg. /en/r/the_matrix or /r/the_matrix)
Router::connect('/:language/r/:slug/*', array('controller'=>'pages', 'action'=>'display'), array('language'=>'[a-z]{2}', 'pass'=>array('slug')));
Router::connect('/r/:slug/*', array('controller'=>'pages', 'action'=>'display'), array('pass'=>array('slug')));
//adds language to default URL
Router::connect('/:language/:controller/:action/*', array(), array('language'=>'[a-z]{2}'));
//Route prefixes
Configure::write('Routing.prefixes', array('admin'));
//User related
Router::connect('/login', array('controller' => 'users', 'action' => 'login'));
Router::connect('/logout', array('controller' => 'users', 'action' => 'logout'));
Router::connect('/myaccount', array('controller' => 'users', 'action' => 'my_account'));
//
CakePlugin::routes();
require CAKE . 'Config' . DS . 'routes.php';
If you don't want to redirect the requestAction calls from within your controllers or views you should add the following condition to your IF statement
if (empty($this->request->params['language']) &&
empty($this->request->params['admin']) &&
empty($this->request->params['requested'])) {
...
}

Pagination and sort - Plugin not working - CakePHP 2.0

I have created a plugin called movies, I have used custom routes.
I have used pagination limit as '5'.
The first page is fine, but when I click on next or numbers. Those things doesn't works.
URL: something.com/movieslist/2
my Plugin/Movies/Config/routes.php
Router::connect('/movieslist', array('plugin' => 'Movies', 'controller' => 'Movies', 'action' => 'index'));
Router::connect('/movieslist/:page', array('plugin' => 'Movies', 'controller' => 'Movies', 'action' => 'index'));
my action code: Plugin/Movies/Controller/MoviesController.php
public function index() {
$this->Movie->recursive = 0;
$this->paginate = array('limit'=>5);
$this->set('movies', $this->paginate());
}
my view file code: Plugin/Movies/View/Movies/index.ctp
Same one from cakebake console. No changes made here.
Even the sort doesn't works :(
I'm tiered of searching my problems in many places :(
I had previously getting error in links itself and I fixed this after seeing this page:
CakePHP custom route pagination
Links are fixed but the links doesn't works. Pls don't down vote, I'm struggling from long time.
I'm using cakephp 2.0 version.
As a first step I'd swap the order of the router rules, since cakephp stops after the first match found. While the "/movieslist/:page" one will match a url with a page, it only does so for the named parameter page. Without it a url of the form "/movielist/2", might be interpreted as a link to "/movielist" with a normal parameter "2", hence the first router rule triggers.
If that does not work you can always just manually set the named page parameter. paginate just looks to see if it is set, but does not care if cakephp automagically figured it out from the url or if you do it yourself.
public function index($myPage=1) {
$this->Movie->recursive = 0;
$this->paginate = array('limit'=>5);
$this->params["page"] = $myPage;
$this->set('movies', $this->paginate());
}

Parse routes in cakephp to load specific model (is it possible to use .htaccess for this?)

First part of this question will look trivial, but the point is in second part.
So, let's say that I have next few links on my app:
http://myapp.com/cars-audi
http://myapp.com/cars-opel
http://myapp.com/cars-fiat
http://myapp.com/cars-vw
In these cases, model car is used. So, in this case, I want to escape using slash in URL.
Then I will have more pages, and URLs, where roads will be involved, like:
http://myapp.com/roads/germany
http://myapp.com/roads/austria
http://myapp.com/roads/hungary
http://myapp.com/roads/poland
So, if it starts with cars-, in that case model cars should be used, and if it starts with roads/, model roads will be in the game.
Is it possible to do with some regular expressions in routes.php, or is it better to load (use) one same model in both cases, and to work with them like that?
Also, is it possible to help parsing URL using .htaccess file?
This is a simple case of routing a URL to a controller action, it doesn't involve models at all.
Router::connect('/:carlink',
array('controller' => 'cars', 'action' => 'view'),
array('carlink' => 'cars-\w+', 'pass' => array('carlink')));
This route says any URL that matches /:carlink should be routed to the given controller and action. In the last part you're clarifying what :carlink can be with the regular expression cars-\w+ ("cars-" followed by any word). You also pass that value to your called action.
class CarsController extends AppController {
public function view($car) {
if (!preg_match('/cars-(\w+)/', $car, $matches)) {
// action was accessed with invalid URL, bail out
$this->cakeError('error404');
}
// use $matches[1], which will be 'audi', for example
…
}
}
Your road URLs would be routed to the RoadsController as usual like this:
Router::connect('/roads/*', array('controller' => 'roads', 'action' => 'view'));

CakePhp Router:connect, I don't get the syntax

I am trying to connect the next urls:
1) /food/tips
2) /happiness/tips/best_tips
To the following objects:
1) controller=tips / action=index / passed_parameters=food
2) controller=tips / action=index / passed_parameters=(happiness,best_tips)
--edit--
These routes are not fixed.
Meaning: what I try to do is to route every url that have tips as action, to the tips controller, to any fixed(index is good enough) action, and chaining the rest of the url as it was in the original call.
Something like /any_controller/tips/any_param to /tips/index/any_params
-- end edit --
Hope that now there is some sense.
How should it be done?
(please - also explain)
Thanks
the routing is all done through the call Router::connect('thing to catch', 'where to send it');
so it can be as simple as:
Router::connect('/food/tips', '/tips/index/food');
or the preferred method (using cakes built in url builder)
Router::connect('/food/tips/*', array('controller' => 'tips', 'action' => 'index', 'food');
The first method takes a string argument and passes it to another string which would be a url and you would then have to catch it in your controller, and expect a passed parameter through the url.
The second method uses cakes built in url former which takes an array with keys controller and action (there are other options: http://api.cakephp.org/class/router#method-Routerurl)
The second is preferred due to proper formatting and future flexibility (I believe).
any passed parameters in the second method are just passed as un-named items in the array. named parameters are just passed as keyed elements. So if I wanted to create a URL like this
/posts/index/find:all/page:2
I would write the url like this:
Router::connect('/url_to_catch', array('controller' => 'posts', 'action' => 'index', 'find' => 'all', 'page' => 2);
So just to finish up, I would actually pass your parameter through as named:
Router::connect('/happiness/tips/best_tips', array('controller' => 'tips', 'action' => 'index', 'items' => array('happiness', 'best_tips'));
which would need a function in your tips controller that looks like this:
function tips(){ $this->passedArgs['items']; }
I would recommend reading the chapter on Routing the the book, as it will explain things better than I can and it seems counter productive to paste it here.
http://book.cakephp.org/#!/view/948/Defining-Routes
For the sake of explanation I will try,
Router::connect('/food/tips', array('controller' => 'tips', 'action' => 'index', 'food'));
Router::connect('/happiness/tips/best_tips', array('controller' => 'tips', 'action' => 'index', 'happiness','best_tips'));
This should get things working for you. What you are essentially doing is telling the Cake Routing what url you want it to capture, as it will be doing this using Regex. Then you want to tell it which code you want it to run. So this takes a Controller and Action pair, as a set of things to do.
You also want to pass through your named paremeters afterwards. These will tack onto the function in your controller so that you can do stuff with them.
It's quite easy, just check the Router configuration in the manual. You have to use the connect method from the Router class. This accepts 2 parameters. First your desired routed (e.g. food/tips) and second an array with the actual path it should follow. So for your examples you'd do something like this:
Router::connect('/food/tips', array('controller' => 'tips', 'action' => 'index', 'food');
Router::connect('/happiness/tips/best_tips', array('controller' => 'tips', 'action' => 'index', 'happiness', 'best_tips');
This is equivalent to calling TipsController->index('food') and TipsController('happiness', 'best_tips) respectively.
However, your routes look a bit funny. The Cake convention for routes is /controller/action/param1/param2/etc where the parameters param1 etc. are optional and the index action is assumed when no other action is given.
You're taking a different approach and I would suggest (if you can) change it to the Cake conventional routes, as this will save you a lot of work later on because Cake will automatically connect these routes to the desired methods.
So my suggestion is going for tips/food and tips/happiness/best_tips instead of the routes you suggest. This way, you don't have to do any router configuration.
UPDATE
After you're edit, I think it's best to try something with defining custom routes. I can't test this for you at the moment, so you should do some testing yourself, but in that case it would be something like:
Router::connect('/:section/tips/:param',
array('action' => 'index'),
array(
'section' => '[a-z]*',
'param' => '[a-z]*'
)
);
UPDATE2
Sorry, I've tested the above and it doesn't seem to work.

Resources