Completely custom path with YII? - yii

I have various products with their own set paths. Eg:
electronics/mp3-players/sony-hg122
fitness/devices/gymboss
If want to be able to access URLs in this format. For example:
http://www.mysite.com/fitness/devices/gymboss
http://www.mysite.com/electronics/mp3-players/sony-hg122
My strategy was to override the "init" function of the SiteController in order to catch the paths and then direct it to my own implementation of a render function. However, this doesn't allow me to catch the path.
Am I going about it the wrong way? What would be the correct strategy to do this?
** EDIT **
I figure I have to make use of the URL manager. But how do I dynamically add path formats if they are all custom in a database?

Eskimo's setup is a good solid approach for most Yii systems. However, for yours, I would suggest creating a custom UrlRule to query your database:
http://www.yiiframework.com/doc/guide/1.1/en/topics.url#using-custom-url-rule-classes
Note: the URL rules are parsed on every single Yii request, so be careful in there. If you aren't efficient, you can rapidly slow down your site. By default rules are cached (if you have a cache setup), but I don't know if that applies to dynamic DB rules (I would think not).

In your URL manager (protected/config/main.php), Set urlFormat to path (and toptionally set showScriptName to false (this hides the index.php part of the URL))
'urlManager' => array(
'urlFormat' => 'path',
'showScriptName'=>false,
Next, in your rules, you could setup something like:
catalogue/<category_url:.+>/<product_url:.+> => product/view,
So what this does is route and request with a structure like catalogue/electronics/ipods to the ProductController actionView. You can then access the category_url and product_url portions of the URL like so:
$_GET['category_url'];
$_GET['product_url'];
How this rule works is, any URL which starts with the word catalogue (directly after your domain name) which is followed by another word (category_url), and another word (product_url), will be directed to that controller/action.
You will notice that in my example I am preceding the category and product with the word catalogue. Obviously you could replace this with whatever you prefer or leave it out all together. The reason I have put it in is, consider the following URL:
http://mywebsite.com/site/about
If you left out the 'catalogue' portion of the URL and defined your rule only as:
<category_url:.+>/<product_url:.+> => product/view,
the URL Manager would see the site portion of the URL as the category_url value, and the about portion as the product_url. To prevent this you can either have the catalogue protion of the URL, or define rules for the non catalogue pages (ie; define a rule for site/about)
Rules are interpreted top to bottom, and only the first rule is matched. Obviously you can add as many rules as you need for as many different URL structures as you need.
I hope this gets you on the right path, feel free to comment with any questions or clarifications you need

Related

How do I enable query string in Cloudflare page rules?

I want to forward this URL
https://demo.example.com/page1?v=105
to
https://www.example.com/page1
It's not working if I set so:
But if I remove the ?v= part, it works:
Is there any way to include the ?v= part in this page rule?
The rule you mentioned works only for that exact URL: https://demo.example.com/page1?v=105. If it has any additional query parameter or even an additional character, eg https://demo.example.com/page1?v=1050, it won't match the rule.
If you need to use complicated URL matches that require specific query strings, you might need to use bulk redirects or a Cloudflare worker.

Yii2 routing: url-rule not working when the "/index"-action is not explicitly specified

This route need a little extra work in order for it to work properly, but I am not able to see what I need to do:
'http://<caregiverName:\w+>.' . $domain . '/<controller:\w+>/<action:\w+>' => '<controller>/<action>',
When I make call like this controller/action, everything works fine. When I make this call controller (without the explicit index) in order to make call to actionIndex, I am not able to catch caregiverName param. But when I make this call controller/index (index explicitly specified), it works normally.
What I need to rework in the router?
Two things:
Rule for calls without index
You need a second rule with the index as a static value when it is not provided. Add this after your first rule like so:
'http://<caregiverName:[\w-.]+>.' . $domain . '/<controller:\w+>/<action:\w+>' => '<controller>/<action>',
'http://<caregiverName:[\w-.]+>.' . $domain . '/<controller:\w+>' => '<controller>/index',
The reason for this is that yii will to this normally, but not for your explicitly defined rule. The reason to put it after is that it would otherwise match before the other and always put you on the index page ;-).
Regex corrections for domain-characters
In you rule you use \w+ as a placeholder for a domain name. the way you specified your rule, it won't work with any other characters than "word-characters" (a-zA-Z0-9_). The dot (subdomain) and the dash (-) are missing! For the domain perfectly.valid-subdomain.com your rule won't work. You can find an axplanation of the regex shorthands here.
Check out my example above how I specified the character classes. You could of course also validate the length etc, but this is not the scope of this answer...you should get the gist.
Tell me if you need more information!

Rails route to catch everything except assets

I'm trying to allow admin to create pages on the root path. So far i have:
get ':path' => "pages#show" ,:as =>:page, :path => /[^\.]+/
Basically i'm trying to ignore all paths with a dot in them (like .png). This does not seem to work as everything is rejected (i only want things in the public directory to be rejected, like fonts, icons, images..)
Thanks
As I explained in my comment above, "everything in public is directly rendered by the webserver" is NOT true if the desired asset does not exist. This will result in your catch-all route catching this undesired side-effect. This could cause a number of problems, as I explained. So, A specific catch-all route is needed to compensate for this:
get ':path' => "pages#show", :as => :page, :constraints => lambda{|req| req.path !~ /\.(png|jpg|js|css)$/ }
you can manipulate the regex how ever you see fit as my goal was just to get you on the right track by showing you that you can pass a block to the :constraints option. Also, I didn't just test req.format because that would exclude requests with header information for js format and would result in the catch all not working for these types of requests (not a usual case for a catch-all, but that's irrelevant). By using req.path instead, the header info is left intact/working and the path dictates whether or not this request is caught by this route.
I hope this helps you.
TESTING:
To test to see if your catch-all is actually catching what you want and not additional public resources, follow these steps. First put a debugger in your catch-all action, in your PagesController. Then make a request to a public file png/js/css file that DOES exist, like localhost:3000/images/example_image.png, and it should not hit your catch-all, as usual. Now, change the path to an image that doesn't exist, localhost:3000/images/no_image.png . If the request does not hit your debugger, your catch-all is not catching the image file request, and your ALL SET. If the request does hit your debugger, that means your catch-all is catching the image file request which means you need to revise your constraints in your catch-all.
By default dynamic segments don’t accept dots – this is because the
dot is used as a separator for formatted routes. If you need to use a
dot within a dynamic segment add a constraint which overrides this –
for example :id => /[^/]+/ allows anything except a slash.
http://guides.rubyonrails.org/routing.html#bound-parameters
So just removing the condition works. There might be another better solution to this problem though.

Pretty URL & SEO-friendly URL?

I'm currently using Apache 2.2
I can do simple things like
RewriteRule ^/news$ /page/news.php [L]
RewriteRule ^/news/(.*)$ /page/news.php?id=$1 [L]
but what if I want to send 2 parameters like this
http://www.example.com/link/param1/param1_value/param2/param2_value
Lastly, I want to also know implementing SEO friendly URL like stackoverflow
though I can get access to a page with URL like
http://www.example.com/doc_no/
Just decorating that URL with
http://www.example.com/doc_no/this-is-the-article
Give me some suggestion with example snippets.
I know that the PHP symfony framework allows you to do that.
How does it work :
In apache config, use mod_rewrite to redirect ALL incoming resquest to a single entry point (in symfony this is called the "front controller")
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
In this front controller you are going to create a "Request" object which holds all the informations provided by the URL.
For example you could say that the first thing after the "/" is the name of the PHP file to call and everything else are parameters and values so that :
http://example.com/file/id/2 will call file.php with id=2
To do that, just use some reg exp and design you "Request" class carefully. For the example above the "Request" class should provide both getRequestedAction() and getParameter(string parameter) methods. The getRequestedAction() method will be used when the "Request" object is fully populated in order to call the correct file/action/method.
if you choose to populate the parameter array of the request object with both reg exp on the URL and a parsing of the _GET array, you may get to the point where :
http://example.com/file/id/2 is the same as http://example.com/file?id=2
(and both can work)
you can choose to ignore extensions (http://example.com/file.html is the same as http://example.com/file), or not.
Finally, for some URL, you can choose to just ignore everything that goes after the last '/'. So that : http://example.com/question/3/where-is-khadafi is the same as http://example.com/question/3/is-linux-better-than-windows
In the different file.php, just use $request->getParameter('id') to get the value of the "id" parameter, instead of using the _GET or _POST arrays.
The whole point is to
Redirect all incoming traffic to a single "front controller"
In that file, create a "Request" object that contains all the informations needed to run the site
Call the correct action (php file) based on the informations contained in that "Request" object
Inside the actions, use this request object to fetch the parameters contained in the URL
Hope this helps
Note Google have stated that they prefer news.php?id=$1 instead of news/$1 because it is easier for them to detect the variable. This is more pertinent when increasing the number of variables as just looking at your first example is a bit confusing:
http://www.example.com/link/param1/param1_value/param2/param2_value
You can always combine the two if one parameter is generic like a category:
http://www.example.com/param1/?id=param2_value
One should really reevaluate the design if more than one parameter is required and it is not a temporary search.

Controller not found issue when rewriting url with exclamation mark

In monorail I'm trying to create a url rewriting rule to give friendly urls to article posts. Here's what the urls look like:
http://domain.com/2010/11/29/Winter-snow-warning
And here's the code in global.asax.cs to rewrite the urls:
RoutingModuleEx.Engine.Add(
new PatternRoute("/<year>/<month>/<day>/<title>")
.DefaultForController().Is("post")
.DefaultForAction().Is("show")
.Restrict("year").ValidInteger
.Restrict("month").ValidInteger
.Restrict("day").ValidInteger
);
This works great, however if there is an exclamation point in the url:
http://domain.com/2010/11/29/Winter-snow-warning!!
Then it doesn't match the rewriting rule and errors out, saying the controller "2010" cannot be found. What am I missing here, is this a bug in monorail?
Perhaps the default matching mechanism of Monorail's routing is not accepting exclamation mark, thus the route does not match and the default /controller/action rule is matched instead, failing since no 2010 controller exists.
A quick workaround could be to to restrict the title to the exact expression that fits your needs. e.g.: .Restring("title").ValidRegex("[-_.+!*'() a-zA-Z0-9]+]")