Custom CodeIgniter route regex - regex

Trying to improve a custom codeigniter route regex that I have created. Essentially the purpose of the custom route is to create a cleaner/shorter URL for client profile pages which have the format of clients/client-slug, for example: clients/acme-inc. I only want this route to match if their are no additional segments after the client-slug segment, and if they client-slug value does not match any of the 'reserved' values which correspond to actual methods/routes in the Clients controller. Currently, this is what I'm using:
$route['clients/(?!some_method|another_method|foo|bar)(.+)'] = 'clients/index/$1';
This mostly works ok, except for when there is a client-slug that begins with one of the reserved methods text, i.e. clients/food-co, which since it has clients/foo in it, the custom route is not matched. So I need to basically conditionally allow the route to contain any of the reserved methods in that set ONLY IF it is followed by additional characters (that is not a /).

Do you try this?
$route['clients/(?!(?:some_method|another_method|foo|bar)(?:/|$))(.+)'] = 'clients/index/$1';

You should consider the _remap() method in the future. It will allow you to update your controller and add new methods without needing to update your route (you actually wouldn't need a route at all, so long as your URI matches the controller name).

Related

Coldbox routing dynamic number of path variables

I am working on a coldbox application where I would like to create a route that accepts 'n' number of path variables as one variable. Here is what I mean.
http://localhost/api/variable1/variable2/variable3/...
I would like to either be able to grab everything after /api as one path variable where I can split on / and get the values or be able to iterate over all variables after /api.
Is there a way to setup a Route to do this?
with(pattern="/api", handler="api")
.addRoute(pattern="/:variables", action="index")
.endWith();
Any ideas would be most appreciated. Thanks in advance.
As you probably know, the default routing paradigm is to do name value pairs like so:
http://localhost/api/name1/value1/name2/value2/name3/value3
There is no need to create a custom route for that as everything after the matched part of the route is broken up into name/value pairs and placed in the rc automatically.
Now, it sounds like you're wanting to only have values in your route. If you know the maximum number of variables you'll ever have, you could create a route of optional, incrementally-named variables.
addRoute(pattern="/:var1?/:var2?/:var3?/:var4?/:var5?", action="index")
Now, if you truly might have an unlimited number of variables, there is no way to do a route that will match that. What you CAN do is have your route match the /api bit and write an onRequestCapture interceptor that grabs the URL and does your own custom parsing on it. Note, you may need to remove the name/value pairs that ColdBox will try to put in the rc.
I will add a note of caution-- the only way for this to really work is for you to KNOW the order of the incoming variables ahead of time, and if you know that, there is no reason why you can't create a known route for it. Otherwise you're basically rebuilding the SES interceptor over again which is an anti-pattern called "inner platform effect"
http://wiki.coldbox.org/wiki/URLMappings.cfm#URL_Mappings
http://wiki.coldbox.org/wiki/Interceptors.cfm#Core_Interception_Points
http://en.wikipedia.org/wiki/Inner-platform_effect

How to match multiple segments in CodeIgniter routing

I'm trying to re-route my CodeIgniter URIs by changing the controller names only and leaving ANY OTHER SEGMENTS intact, regardless of how many there are (if any).
My controllers are using a single-level folder structure, such as controllers/user/profile_controller.php.
Each controller is called [name]_controller to distinguish it from other files/classes and avoid conflicts (ie. Users controller clashing with Tank Auth users model), but I want the URI to be:
/users/profile
Therefore, a simple route (that works) would be:
$route['(:any)/(:any)'] = '$1/$2_controller';
But the above doesn't allow for subsequent segments, and I don't know how many there could be. (:any) doesn't work because it only applies to single segments and obviously I don't want to write several routes for the potential number of segments, even if it is low.
I have tried a regular expression to match the rest of the URI (eg: users/profile/edit/123/abc), but the following doesn't work:
$route['(:any)/(:any)/(.+)'] = "$1/$2_controller/$3";
Does anyone know if it is possible to match the remaining segments and put them back onto the re-routed URI?
Thanks in advance.
Mat

CodeIgniter routing

I need to write a CI route so that it loads the right controller. What I want to do is write a route that excludes the "features" controller. Here are my routes (but the first one doesn't work).
$route['(\w{2})/(\w{2})/products/([\w]+!features)'] = "products/products/$3"; // folder/controller/method
$route['(\w{2})/(\w{2})/products/features/([\w]+)'] = "products/features/$3"; // folder/controller/method
What I want to have happen is the top line should load any controller that is not the features controller. But I am getting conflicts between the two lines. I've tried placing the "!features" in a couple of different places in the line, with and without quotes and I'm still getting either the features controller to load or one of the other methods in the products controller to load. But not both. Can someone help out here? Thanks.
Try changing the first line to:
$route['(\w{2})/(\w{2})/products/(?!features$)(\w+)'] = "products/products/$3";
(?!foo) is a negative lookahead, if fails if whatever is following it matches foo.
$ means end of string, and is there to make sure that features is not part of a longer word.

Django reverse routing - solution to case where reverse routing is less specific than forward routing

I have a route defined as follows:
(r'^edit/(\d+)/$', 'app.path.edit')
I want to use the reverse function as follows:
url = reverse('app.path.edit', args=('-id-',))
The generated url gets passed to a js function, and client side code will eventually replace '-id-' with the correct numeric id. This of course won't work, because the 'reverse' function won't match the route, because the url is defined as containing a numeric argument.
I can change the route to accept any type of argument as follows, but then I loose some specificity:
(r'^edit/(.+)/$', 'app.path.edit'
I could create a separate url for each item being displayed, but I'll be displaying many items in a list, so it seems like a waste of bandwidth to include the full url for each item.
Is there a better strategy to accomplish what I want to do?
You can rewrite regexp like this:
(r'^edit/(\d+|-id-)/$', 'app.path.edit')
but I generally prefer this:
(r'^edit/([^/]+)/$', 'app.path.edit') # you can still differ "edit/11/" and "edit/11/param/"
Usually you will anyway need to check entity for existent with get_object_or_404 shortcut or similar, so the only bad is that you have to be more accurate with incoming data as id can contain almost any characters.
In my opinion, and easier solution would be to keep the original url and then pass the value '0' instead of '-id-'. In the client side then you replace '/0/' with the correct id. I think this is better because it doesn't obscure the url routing, and you don't lose specificity.

Match all characters in group except for first and last occurrence

Say I request
parent/child/child/page-name
in my browser. I want to extract the parent, children as well as page name. Here are the regular expressions I am currently using. There should be no limit as to how many children there are in the url request. For the time being, the page name will always be at the end and never be omitted.
^([\w-]{1,}){1} -> Match parent (returns 'parent')
(/(?:(?!/).)*[a-z]){1,}/ -> Match children (returns /child/child/)
[\w-]{1,}(?!.*[\w-]{1,}) -> Match page name (returns 'page-name')
The more I play with this, the more I feel how clunky this solution is. This is for a small CMS I am developing in ASP Classic (:(). It is sort of like the MVC routing paths. But instead of calling controllers and functions based on the URL request. I would be travelling down the hierarchy and finding the appropriate page in the database. The database is using the nested set model and is linked by a unique page name for each child.
I have tried using the split function to split with a / delimiter however I found I was nested so many split statements together it became very unreadable.
All said, I need an efficient way to parse out the parent, children as well as page name from a string. Could someone please provide an alternative solution?
To be honest, I'm not even sure if a regular expression is the best solution to my problem.
Thank you.
You could try using:
^([\w-]+)(/.*/)([\w-]+)$
And then access the three matching groups created using Match.SubMatches. See here for more details.
EDIT
Actually, assuming that you know that [\w-] is all that is used in the names of the parts, you can use ^([\w-]+)(.*)([\w-]+)$ instead and it will handle the no-child case fine by itself as well.