Custom routes in CakePHP: regex not limiting matches - php

I'm trying to configure my custom routes in cakephp such that the url
/objects/id/action => ObjectsController.action() with params['id']=id
(This is so that I don't have to have urls like /objects/action/id which logically make less sense to me than objects/id/action).
I still want /objects/action to trigger ObjectsController.action() (e.g. for add, index, search).
My routes config looks like this:
Router::connect('/:controller/:id',
array('action'=>'view'),
array(
':id' => '^[0-9]+$'
)
);
Router::connect('/:controller/:id/:action/*',
array('action'=>'view'),
array(
':id' => '^[0-9]+$',
':action' => '[A-Za-z0-9_\-]*'
)
);
This works with (for example):
/objects/54
/objects/54/edit
/objects/add
But not with
/objects/index/page:2
For which it gives me the error that I need to define the action "page:2" in ObjectsController... Surely it should work, because :id should only match digits, no?

Try to remove ":" from second param:
'id' => '^[0-9]+$'
Also see 'pass' option.
#see google on "cakephp routes":
http://book.cakephp.org/view/945/Routes-Configuration

Related

Prefixing a company name to URL in CakePHP

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

Zend route regex, optional parameters

I need some help about my routes in Zend (Zend1) ...
I need that I can use my route like that :
http://mywebsite.com/myfolder/region/job/
http://mywebsite.com/myfolder/region/job/add
http://mywebsite.com/myfolder/region/job/add/page
http://mywebsite.com/myfolder/region/job/page
Parameters add and page are optional ...
This is what I did
$route = new Zend_Controller_Router_Route_Regex(
'myfolder/([^/]+)/([^/]+)/?([^/]+)?/?([0-9]+)?',
array('controller' => 'myfolder','action' => 'search'),
array(1 => 'region',2 => 'job', 3 => 'add', 4 => 'page'),
'myfolder/%s/%s/%s/%s'
);
Obviously, it doesn't work ...
What I want? I want that last the two parameters (add and page) are optional ...
Can you help me? what's wrong with my regex?
EDIT 1:
Ok, so I tried it, but isn't ok ...
I need that parameters add and page are optional ...
$route = new Zend_Controller_Router_Route(
'myfolder/:region/:job/:add/:page',
array(
'controller' => 'myfolder',
'action' => 'search',
'region' => 'XX',
'job' => '',
'add' => '',
'page' => 1
),
array(
'region' => '[a-zA-Z-_0-9-]+',
'job' => '[a-zA-Z-_0-9-]+',
'add' => '[a-zA-Z-_]+',
'page' => '\d+'
)
);
With that, this one http://mywebsite.com/myfolder/region/job/page doesn't work ...
EDIT 2:
I also tried with 'myfolder/:region/:job/*', but the result is same, doesn't work as I want ...
I really wonder if it is possible ...
EDIT 3:##
$route = new Zend_Controller_Router_Route_Regex('myfolder/([^/]+)/([^/]+)(?:/|$)(?:(?!\d+(?:/|$))([^/]+)(?:/|$))?(?:(\d+)(?:/|$))?$',
array('controller' => 'myfolder', 'action' => 'recherche', 'presta' => ''),
array(1 => 'region',2 => 'job', 3 => 'presta', 4 => 'page'),
'myfolder/%s/%s/%s/%s');
Prepare yourself.
The RegEx
myfolder/([^/]+)/([^/]+)(?:/|$)(?:(?!\d+(?:/|$))([^/]+)(?:/|$))?(?:(\d+)(?:/|$))?$
See it working on RegExr (on RegExr, I had to add \n\r to one of the negated classes so it didn't match all my line breaks, in practice you probably won't be dealing with line breaks though.)
The important thing to note on RegExr is that in the 4th case, the page number is in the 4th capture group, with nothing in the 3rd group.
Explanation
myfolder/([^/]+)/([^/]+) All looking good up to here, no changes yet.
(?:/|$) Match a / or end of input.
Next, overall we have a non-capturing group that is optional. This would be the add section.
(?:(?!\d+(?:\|$))([^/]+)(?:/|$))?
Now lets break it down further:
(?!\d+(?:/|$)) Make sure its not a page number - digits only followed by / or end of input. (Negative lookahead)
([^/]+) Our capture group - add in the example.
(?:/|$) Match a / or end of input.
Now for our page number group, again it's optional and non-capturing:
(?:(\d+)(?:/|$))? Captures the numbers, then matches / or end of input again.
$ And just in case it tries to match substrings of actual matches, I threw in another end of input anchor (since you can match as many in a row as you like), although the regex functions without it.
Generating The Path
What you basically want is a way of doing this:
At the moment the 2nd and 3rd parameters are:
array(1 => 'region',2 => 'job', 3 => 'add', 4 => 'page'),
'myfolder/%s/%s/%s/%s'
You want them to be something like:
array(1 => 'region',2 => 'job', 3 => '/'+'add', 4 => '/'+'page'),
'myfolder/%s/%s%s%s'
Where you only add the / if the optional group is present. The code above won't work but perhaps there is some way you could implement that.

understanding ElasticSearch routing

I am trying to use the elasticsearch routing mapping to speed up some queries, but I am not getting the expected result set (not worried about the query performance just yet)
I am using Elastic to set up my mapping:
$index->create(array('number_of_shards' => 4,
'number_of_replicas' => 1,
'mappings'=>array("country"=>array("_routing"=>array("path"=>"countrycode"))),
'analysis' => array(
'analyzer' => array(
'indexAnalyzer' => array(
'type' => 'keyword',
'tokenizer' => 'nGram',
'filter' => array('shingle')
),
'searchAnalyzer' => array(
'type' => 'keyword',
'tokenizer' => 'nGram',
'filter' => array('shingle')
)
)
) ), true);
If I understand correctly, what should happen is that each result should now have a field called "countrycode" with the value of "country" in it.
The results of _mapping look like this:
{"postcode":
{"postcode":
{"properties":
{
"area1":{"type":"string"},
"area2":{"type":"string"},
"city":{"type":"string",
"include_in_all":true},
"country":{"type":"string"},
"country_iso":{"type":"string"},
"country_name":{"type":"string"},
"id":{"type":"string"},
"lat":{"type":"string"},
"lng":{"type":"string"},
"location":{"type":"geo_point"},
"region1":{"type":"string"},
"region2":{"type":"string"},
"region3":{"type":"string"},
"region4":{"type":"string"},
"state_abr":{"type":"string"},
"zip":{"type":"string","include_in_all":true}}},
"country":{
"_routing":{"path":"countrycode"},
"properties":{}
}
}
}
Once all the data is in the index if I run this command:
http://localhost:9200/postcode/_search?pretty=true&q=country:au
it responds with 15740 total items
what I was expecting is that if I run the query like this:
http://localhost:9200/postcode/_search?routing=au&pretty=true
Then I was expecting it to respond with 15740 results
instead it returns 120617 results, which includes results where country is != au
I did note that the number of shards in the results went from 4 to 1, so something is working.
I was expecting that in the result set there would be an item called "countrycode" (from the rounting mapping) which there isn't
So I thought at this point that my understand of routing was wrong. Perhaps all the routing does is tell it which shard to look in but not what to look for? in other words if other country codes happen to also land in that particular shard, the way those queries are written will just bring back all records in that shard?
So I tried the query again, this time adding some info to it.
http://localhost:9200/postcode/_search?routing=AU&pretty=true&q=country:AU
I thought by doing this it would force the query into giving me just the AU place names, but this time it gave me only 3936 results
So I Am not quite sure what I have done wrong, the examples I have read show the queries changing from needing a filter, to just using match_all{} which I would have thought would only being back ones matching the au country code.
Thanks for your help in getting this to work correctly.
Almost have this working, it now gives me the correct number of results in a single shard, however the create index is not working quite right, it ignores my number_of_shards setting, and possibly other ones too
$index = $client->getIndex($indexname);
$index->create(array('mappings'=>array("$indexname"=>array("_routing"=>array("required"=>true))),'number_of_shards' => 6,
'number_of_replicas' => 1,
'analysis' => array(
'analyzer' => array(
'indexAnalyzer' => array(
'type' => 'keyword',
'tokenizer' => 'nGram',
'filter' => array('shingle')
),
'searchAnalyzer' => array(
'type' => 'keyword',
'tokenizer' => 'nGram',
'filter' => array('shingle')
)
)
) ), true);
I can at least help you with more info on where to look:
http://localhost:9200/postcode/_search?routing=au&pretty=true
That query does indeed translate into "give me all documents on the shard where documents for country:AU should be sent."
Routing is just that, routing ... it doesn't filter your results for you.
Also i noticed you're mixing your "au"s and your "AU"s .. that might mix things up too.
You should try setting required on your routing element to true, to make sure that your documents are actually stored with routing information when being indexed.
Actually to make sure your documents are indexed with proper routing explicitly set the route to lowercase(countrycode) when indexing documents. See if that helps any.
For more information try reading this blog post:
http://www.elasticsearch.org/blog/customizing-your-document-routing/
Hope this helps :)

Zend pagination and routing

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_-\.]+).

CakePHP Routing and named parameters

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'
)
);

Categories