In this article, we introduce an approach that allows automatic hyphenation of the route part in URLs.
As we know, Yii uses the URL manager to support URL creation and parsing. However, the default implementation does not deal well with the route that has mixed cases. For example, given a route /createAccount, the URL manager would generate the following URL by default:
/user/createAccount
For SEO purists, this is not very pretty. They would want something like /user/create-account for better readability. To achieve this goal, we can add the following rule when configuring the URL manager:
'user/create-account' => 'user/createAccount'
This is fine but not perfect, because it requires us to specify a similar rule for every route that has mixed cases. To avoid this trouble and also to improve the performance, we can extend CUrlManager as follows,
class UrlManager extends CUrlManager { public $showScriptName = false; public $appendParams = false; public $useStrictParsing = true; public $urlSuffix = '/'; public function createUrl($route, $params = array(), $ampersand = '&') { $route = preg_replace_callback('/(?<![A-Z])[A-Z]/', function($matches) { return '-' . lcfirst($matches[0]); }, $route); return parent::createUrl($route, $params, $ampersand); } public function parseUrl($request) { $route = parent::parseUrl($request); return lcfirst(str_replace(' ', '', ucwords(str_replace('-', ' ', $route)))); } }
In the above, we define a new class UrlManager which extends from CUrlManager. We mainly override the createUrl() and parseUrl() methods to perform the hyphenation of routes. We also override the default values of several properties of CUrlManager so that the URLs are even more SEO friendly.
Now we need to make some minor changes to the application configuration:
return array( // .... 'components' => array( 'urlManager' => array( 'class' => 'UrlManager', 'rules' => array( // .... '<controller:[\w\-]+>/<action:[\w\-]+>' => '<controller>/<action>', ), ), ), );
In the above, we mainly specify the class of urlManager to be our new class UrlManager. We also change the rule a little bit so that it can match the hyphens (-) in the URLs (the default setting only matches word characters which don't include hyphens).
With these code in place, for the route user/createAccount we would obtain URL /user/create-account/. The ending slash is because we set urlSuffix to be / in UrlManager.
Note: Because the above code uses anonymous function and
lcfirst(), it requires PHP 5.3 or above.
Total 4 comments
Qiang, can you please explain, why do you force
useStrictParsingto be set toTRUE? If this is enabled, it causes problems with default controllers and/or actions not being executed correctly, as described in here.Setting this parameter to
FALSE(default value forCUrlManager.useStrictParsing) seems to be solving the problem and solution from your article also seems to be working fine without it. So, I think it is not required / not necessary here.Also, tell us if using
urlSuffixis obligatory? Or is it just an addition. I'm not using it in my rules, so I disabled it and solultion also seems to be working fine without it.EDIT: After testing this for a few days, I can say that presented solution works like a charm without
useStrictParsingandurlSuffix.Using method mentioned in this article is prefered one, as in OOP manner.
But, if someone would prefer your solution, here is CController.missingAction description to read about.
Another approach can be to add a 'missingAction' method in the controller class where you need to have hyphen/dash using action names. This method is called whenever Yii doesn't find an action with the name it parsed.
Sample method:
Credit - this kind of method (or similar) was suggested in Yii's forums. I don't have the URL for it at the moment.
I've been thinking about the regexp:
The negative lookbehind would match a capital letter not preceeded by a capital letter. A class name 'camelXHtmlTest' would turn into 'camel-xHtml-test'. In order to be 'camel-x-html-test', the pattern should become:
Am I missing something? Thanks.
Edit: Think I got it... The patterns avoids many hyphens if the class name contains more consecutive capital letters, e.g. "camelXHTMLTest". Conslusion: follow the convention of not using consecutive capital letters, so the class should better be "camelXhtmlTest" and then one may go for the simpler pattern.
Leave a comment
Please login to leave your comment.