I am using CakePHP 2.5.x and is working fine with default routing. Now, I'd like to prefix a company name parameter that will vary based on user logged in.
For instance, currently it works this way
http://localhost/Pages/view
http://localhost/Pages/add
I would like it to work this way
http://localhost/acme/Pages/view
http://localhost/acme/Pages/add
I have already configured routes.php configuration file to take care of the actual routing above with the following lines
/**
* Catch urls like http://www.domain.com/<company>/<controller>/<action>/<parameters>
*/
Router::connect(
'/:company/:controller/:action/*',
array (),
array (
'company' => '[a-zA-Z0-9]+' // regular expression match for the company parameter
)
);
/**
* Catch urls like http://www.domain.com/<company>/<controller>/<action>
*/
Router::connect(
'/:company/:controller/:action',
array (),
array (
'company' => '[a-zA-Z0-9]+' // regular expression match for the company parameter
)
);
/**
* Catch urls like http://www.domain.com/<company>/<controller>/
*/
Router::connect(
'/:company/:controller',
array (
'action' => 'index'
),
array (
'company' => '[a-zA-Z0-9]+' // regular expression match for the company parameter
)
);
/**
* Catch urls like http://www.domain.com/<company>/
*/
Router::connect(
'/:company',
array(
'controller' => 'Users', // default controller
'action' => 'index' // default action
),
array (
'company' => '[a-zA-Z0-9]+' // regular expression match for the company parameter
)
);
The problem I have is that in many places in my application I have used echo Router::url('') and now all those links will need to be updated. Is there some universal way that I can ensure that once a user is logged in, all links generated for that user will obtain the relevant company name and prefix with that?
Help is appreciated.
What you are looking for is the persist option, it defines which parameters that are present in the current URL should automatically be included when generating new URLs.
Example:
Router::connect(
'/:company/:controller/:action/*',
array (),
array (
'company' => '[a-zA-Z0-9]+',
'persist' => array(
'company'
)
)
);
In case the current URL is /acme/pages/view, the following call
Router::url(array(
'controller' => 'pages',
'action' => 'add'
));
would generate a URL including the company name like
/acme/pages/add
Likewise the company name would not be included in case it's not present in the current URL, ie on /pages/view the generated URL would be
/pages/add
In case you need to include the company name before you can issue a redirect so that the company name is present in the URL, you could simply add it to the current request, for example in your controller
$this->request->params['company'] = 'acme';
From there on the generated URLs will contain the company name.
See also http://book.cakephp.org/2.0/en/development/routing.html#Router::connect
Calculate a $current_company variable in the AppController.php in the beforeFilter method:
public function beforeFilter() {
$current_company = 'acme'; //just assigning a value, you can determine this variable in any way
$this->set(compact('current_company'));
}
then, if your views, declare the company value in the links, like:
<?php echo $this->Html->link(__('Link Name'),array('company' => $current_company,'controller' => 'YOUR_CONTOLLER', 'action' => 'YOUR_ACTION'), array('css' => 'my_style')); ?>
or
Link Name
or
echo Router::url(array('company' => $current_company,'controller' => 'YOUR_CONTOLLER', 'action' => 'YOUR_ACTION'))
Cake should automagically add the variable as prefix like /acme/YOUR_CONTOLLER/YOUR_ACTION
Related
I have a site that needs to allow multiple URL structures. For example:
www.examplesite.com/people/add // <-- example company
www.otherexample.com/xyz/people/add // <-- "xyz" company (not location based)
www.otherexample.com/florida/abc/people/add //<-- "abc" company (location based)
Each URL should be able to detect which company it is based on the URL.
So far, I've been able to parse out the URL just fine to determine which company it is, but how to I add these extra /florida/abc/ parts to the routes to allow the rest of the app to work?
I've tried a number of things including setting a variable to the '/florida/abc' (or whatever it is) at the top of the routes file, then adding that before each route, but that doesn't handle every controller/action and seems very hit or miss/buggy.
I also use the admin prefix, so for example, it would also need to work like this:
www.otherexample.com/admin/florida/abc/people/add
My assumption is that I need to use the routes.php file, but I can't determine within that how I can make this happen.
I used that approach in the web application farm.ba (not more maintained by the owner).
What I did:
Create table "nodes" with fields id, slug, model, foreign_key, type, ..
Create custom route (1),(2) class that handles Node model
After save post, store and cache slug in Node Model
After deleting the post, delete the cache and node records
This works much like wordpress routing, allows you to enter custom slug, etc.
EDIT:
Create custom route class in App/Lib/Routing/Router/MultiRoute.php like:
<?php
App::uses('CakeRoute', 'Routing/Route');
/**
* MultiRoute
*/
class MultiRoute extends CakeRoute
{
public function parse($url)
{
// debug($url); '/florida/abc/people/add'
// Add custom params
$params = array(
'location' => null,
'company' => null,
'controller' => 'peoples',
);
$params += parent::parse($url);
// debug($params);
/**
* array(
* 'location' => null,
* 'company' => null,
* 'controller' => 'peoples',
* 'named' => array(),
* 'pass' => array(
* (int) 0 => 'florida', // location
* (int) 1 => 'abc', //company
* (int) 2 => 'people', // controller
* (int) 3 => 'add' // action, default index
* ),
* 'action' => 'index',
* 'plugin' => null
* )
*
*/
// reverse passed params
$pass = array_reverse($params['pass']);
// debug($pass);
/**
* array(
* (int) 0 => 'add',
* (int) 1 => 'people',
* (int) 2 => 'abc',
* (int) 3 => 'florida'
* )
*/
if(isset($pass[3])) { $params['location'] = $pass[3]; }
if(isset($pass[2])) { $params['company'] = $pass[2]; }
// if you need load model and find by slug, etc...
return $params;
}
public function match($url)
{
// implement your code
$params = parent::match($url);
return $params;
}
}
in routes.php
App::uses('MultiRoute', 'Lib/Routing/Route');
Router::connect('/admin/*',
array('admin' => true),// we set controller name in MultiRoute class
array('routeClass' => 'MultiRoute')
);
Router::connect('/*',
array(),// we set controller name in MultiRoute class
array('routeClass' => 'MultiRoute')
);
In your controller find results using extra request params, like:
$this->request->location;
$this->request->company;
I hope this is helpful.
Creating a route for each case seems the way to go:
Router::connect('/people/add', array('controller' => 'people', 'action' => 'add'));
Router::connect('/:company/people/add', array('controller' => 'people', 'action' => 'add'), array('pass' => array('company'), 'company' => '[a-z]+'));
Router::connect('/:location/:company/people/add', array('controller' => 'people', 'action' => 'add'), array('pass' => array('company', 'location'), 'company' => '[a-z]+', 'location' => '[a-z]+'));
Then the controller can receive these values:
public function add($company = '', $location = '') {
var_dump($company, $location); exit;
}
Mind the regex in routes and amend to match your incoming data.
Is it possible to pass multiple parameters to an action in the controller by using the method postLink of the FormHelper?
I didn't manage to do it by using the options array. It is not very well specified in the docs what values this array admits.
This is what I tryed:
$this->Form->postLink($staffUser['User']['_name'], array(
'action' => 'subscribe',
array('ticketId' => $ticket['Ticket']['id'], 'userId' => $staffUser['User']['id'])
));
My subscribe action looks like this:
public function subscribe($ticketId, $userId = null){
if ($this->request->is('post')) {
//...
}
}
Update
I've just noticed the provided solution creates another problem to me. Now the class stop getting added ulike before when I was using only one parameter:
$this->Form->postLink($staffUser['User']['_name'], array(
'action' => 'subscribe',
$ticket['Ticket']['id'],
$staffUser['User']['id'],
array('class' => 'demo') //not beind added
));
postLink takes its URL entry in the same form as the create method. Parameters don't need a key, put them in order after the action key/value.
Other options need to go in the third argument - you have the options array as an additional entry to the url array.
$this->Form->postLink($staffUser['User']['_name'], array(
'action' => 'subscribe',
$ticket['Ticket']['id'],
$staffUser['User']['id']
),
array(
'class' => 'demo'
)
);
See options for FormHelper::create here:
http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html
Having a bit of trouble getting my URLs to work properly.
The URL looks like this: /messages/from/1/page/5
My route looks like this
$router->addRoute('messages-from',
new Zend_Controller_Router_Route('messages/from/:user_id/:page', array(
'controller' => 'messages',
'action' => 'from',
'page' => 1
))
);
Which works fine. But the URL is missing the /page/ part. If I add it in:
'messages/from/:user_id/page/:page'
then it breaks and the user_id param is always null.
How can I fix this?
Thanks!
Since you want to be able to leave off the /page/ part from the URL, you would have to define two separate routes, one that matches the user ID and page parameters and one that only matches the user ID without the page so the router can find route matches in both cases.
Alternatively, this regex based route works in both cases.
$route = new Zend_Controller_Router_Route_Regex(
'messages/from/(\d+)(?:/page/(\d+)/?)?',
array(
'controller' => 'messages',
'action' => 'from',
'page' => 1,
),
array(
1 => 'from',
2 => 'page',
)
);
$router->addRoute('messages-from', $route);
Based on the URL you supplied, I assumed in the regex that the from parameter is an integer. If you can have strings passed, you will need to change the (\d+) pattern to something more suitable like ([\w\d_-\.]+).
I have one route that looks like this:
Router::connect('/Album/:slug/:id',array('controller' => 'albums', 'action' => 'photo'),array('pass' => array('slug','id'),'id' => '[0-9]+'));
and another like this:
Router::connect('/Album/:slug/*',array('controller' => 'albums','action' => 'contents'),array('pass' => array('slug')));
for what doesn't match the first. In the 'contents' action of the 'albums' controller, I take care of pagination myself - meaning I retrieve the named parameter 'page'.
A URL for the second route would look like this:
http://somesite.com/Album/foo-bar/page:2
The Above URL indeed works, but when I try to use the HTML Helper (url,link) to output a url like this, it appends the controller and action to the beginning, like this:
http://somesite.com/albums/contents/Album/foo-bar/page:2
Which i don't like.
The code that uses the HtmlHelper is as such:
$html->url(array('/Album/' . $album['Album']['slug'] . '/page:' . $next))
See below url it is very help full to you
http://book.cakephp.org/2.0/en/development/routing.html
Or read it
Passing parameters to action
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:
<?php
// SomeController.php
public function view($articleId = null, $slug = null) {
// some code here...
}
// routes.php
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]+'
)
);
And now, thanks to the reverse routing capabilities, you can pass in the url array like below and Cake will know how to form the URL as defined in the routes:
// view.ctp
// this will return a link to /blog/3-CakePHP_Rocks
<?php
echo $this->Html->link('CakePHP Rocks', array(
'controller' => 'blog',
'action' => 'view',
'id' => 3,
'slug' => 'CakePHP_Rocks'
));
I have the following route:
Router::connect('/admin/login/:to',
array('admin'=>true,'controller'=>'users','action'=>'login'),
array(
'to' => '[A-Za-z0-9\._-]+',
'pass' => array('to')
));
Which basically passes a string/int with the login url. But it no longer uses the named parameter of to. So for example instead of getting: /admin/login/to:1AB I get /admin/login/1AB
How do I keep the named parameter but still alter the routing to remove the users bit from the url? I've tried: '/admin/login/to::to' but that seems rather sloppy...
you could find passed parameter's name in "$this->data" in your controller.
in your example: $this->data->to has the same value you put in your url.
Remove that route. Why do you have that route when you want the named parameter?
Edit: if so:
Router::connect(
'/admin/login/*',
array(
'admin' => true,
'controller' => 'users',
'action' => 'login'
)
);