前兩天作了網站SEO方面的URL優化工做。 php
具體要求是:商品分類的URL中須要有這個分類的漢語拼音出現, html
如:http://www.abc.com/category-shafa.html web
(以前的URL大體是這樣的:http://www.abc.com/category/index/f/24/c/279/p.html,其中f參數對應分類id ) sql
分析:在分類表裏面添加拼音字段;在url生成以前,將f參數轉換成所需的拼音。在解析URL以前,將拼音轉換成對應的分類id。 數據庫
從哪裏開始入手呢,首先想到的是在main.php中的urlManage配置中德rules數組中添加規則, 數組
難題來了,id轉換成拼音是須要查數據庫的,在main.php中查數據庫只能用原生的sql來查詢,不但影響系統的擴展,還會破壞配置文件的整潔性。 app
因而乎,查了一下Yii的API,發現Yii的擴展性作的至關出色,rules參數裏面還能夠傳入數組,而CUrlManager只是遞歸處理了一下這個rules就解決了數組參數。很佩服yii的設計者,複雜問題卻用如此簡單的幾步就解決了(代碼就不貼出來了)。 yii
下面是API中的參考例子。 函數
//Starting from version 1.1.8, one can write custom URL rule classes and use them for one or several URL rules. For example, array( // a standard rule '<action:(login|logout)>' => 'site/<action>', // a custom rule using data in DB array( 'class' => 'application.components.MyUrlRule', 'connectionID' => 'db', ), ) //Please note that the custom URL rule class should extend from CBaseUrlRule and implement the following two methods, //CBaseUrlRule::createUrl() //CBaseUrlRule::parseUrl()站在巨人的肩膀上,你才能前進的更快,確實如此。
如今只需簡單幾步就能夠完成任務: 優化
一、建立一個MyUrlRule類,繼承自CUrlRule,(API中說是繼承CBaseUrlRule,既然CUrlRule已經繼承自CBaseUrlRule,且功能很完善,因此此處直接繼承CUrlRule )
二、重寫createUrl()、parseUrl()這兩個方法
一步一步來,首先,先建立一個UrlRule類:
class CategoryUrlRule extends CUrlRule{ public function __construct($route, $pattern) { parent::__construct($route, $pattern); } public function createUrl($manager, $route, $params, $ampersand) { return parent::createUrl($manager, $route, $params, $ampersand); } public function parseUrl($manager, $request, $pathInfo, $rawPathInfo) { return parent::parseUrl($manager, $request, $pathInfo, $rawPathInfo); } }這是一個最簡單的UrlRule類,接下來就是:
一、在構造函數中添加rule;
二、在createUrl()中先修改$params(將id轉換爲pinyin,轉換後若是有多餘的參數,能夠unset掉。);
三、在parseUrl()中解析完$params後,添加拼音轉id的代碼。
按照上面的三個步驟,向UrlRule類中添加相應的代碼後,以下所示:(parseUrl()裏面只有漢字註釋中間的部分是本身寫的。裏面最主要的就是上面三個步驟中提到的三個函數,其他的代碼都是爲了完成這三個步驟的輔助代碼,根據本身的數據庫及url規則來自定義 )
<?php /** * 添加新的URL生成規則 * 原理:添加一個新的標識符(1) 或 替換原有標識符(2) * 1) 適用於只需正向解析時,某一分類的商品URL中須要展現分類的漢語拼音時, * 根據分類id添加新的參數(pinyin,動態寫入createUrl()中的參數$params中), * 而後再rules中定義次參數的位置便可 * 2) 若是須要反向解析,須要在parseUrl()函數中添加新的反向解析方法 * * */ class CategoryUrlRule extends CUrlRule{ public static $extParamName = 'iii'; public static $catExtFront = 'category-'; //新添加的url對應關係 public static $_catUrls = array(); //獲取新添加的URL對應關係 public static function getFcByPinyin($show_pinyin){ $category = Category::model()->findByAttributes(array('show_pinyin'=>$show_pinyin)); if($category){ $f = $category->id; $c = $category->property_id; } return array('f'=>$f,'c'=>$c); } //獲取新添加的URL對應關係 public static function getCatUrls(){ if(empty(self::$_catUrls)){ $c = Category::model()->findAll(); $u = CHtml::listData($c, 'id', 'show_pinyin'); self::$_catUrls = $u; } return self::$_catUrls; } /** * return 新添加的rules規則 */ public static function getExtRules(){ return array( self::$catExtFront.'<'.self::$extParamName.':\w+>*' => 'category/index', ); } /** * 驗證是否用心添加的rule規則,由於新的規則要兼容以前的規則 * $params 這個參數可能會增長新的元素 */ public static function addParams($manager, $route, &$params, $ampersand){ foreach ($params as $pk => $pv){ if(!trim($pv)){ unset($params[$pk]); } } $extRules = self::getExtRules(); if(in_array($route, $extRules) && $params){ $iid = isset($params['f']) ? $params['f'] : 0; if($iid){ $ext = self::getCatUrls(); if(in_array($iid, array_keys($ext)) && trim($ext[$iid])){ unset($params['f']); unset($params['c']); $ename = $ext[$iid]; $params[self::$extParamName] = $ename; } } } } public function __construct() { //註冊新的rules $extRules = self::getExtRules(); foreach ($extRules as $pattern => $route){ parent::__construct($route, $pattern); } } public function createUrl($manager, $route, $params, $ampersand) { //驗證是否用新添加的rule規則 self::addParams($manager, $route, $params, $ampersand); return parent::createUrl($manager, $route, $params, $ampersand); } public function parseUrl($manager, $request, $pathInfo, $rawPathInfo) { // return parent::parseUrl($manager, $request, $pathInfo, $rawPathInfo); if($this->verb!==null && !in_array($request->getRequestType(), $this->verb, true)) return false; if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive) $case=''; else $case='i'; if($this->urlSuffix!==null) $pathInfo=$manager->removeUrlSuffix($rawPathInfo,$this->urlSuffix); // URL suffix required, but not found in the requested URL if($manager->useStrictParsing && $pathInfo===$rawPathInfo) { $urlSuffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix; if($urlSuffix!='' && $urlSuffix!=='/') return false; } if($this->hasHostInfo) $pathInfo=strtolower($request->getHostInfo()).rtrim('/'.$pathInfo,'/'); $pathInfo.='/'; if(preg_match($this->pattern.$case,$pathInfo,$matches)) { foreach($this->defaultParams as $name=>$value) { if(!isset($_GET[$name])) $_REQUEST[$name]=$_GET[$name]=$value; } $tr=array(); foreach($matches as $key=>$value) { if(isset($this->references[$key])) $tr[$this->references[$key]]=$value; else if(isset($this->params[$key])) $_REQUEST[$key]=$_GET[$key]=$value; } /** * 根據提交的分類,重置$_GET/$_REQUST */ if(isset($_GET[self::$extParamName])){ $fc = self::getFcByPinyin($_GET[self::$extParamName]); foreach(array('f','c') as $fckey){ $_REQUEST[$fckey]=$_GET[$fckey]=$fc[$fckey]; if(isset($this->references[$fckey])) $tr[$this->references[$fckey]]=$fc[$fckey]; else if(isset($this->params[$fckey])) $_REQUEST[$fckey]=$_GET[$fckey]=$fc[$fckey]; } } /** * 完成重置$_GET/$_REQUST */ if($pathInfo!==$matches[0]) // there're additional GET params $manager->parsePathInfo(ltrim(substr($pathInfo,strlen($matches[0])),'/')); if($this->routePattern!==null) return strtr($this->route,$tr); else return $this->route; } else return false; } } ?>而後在main.php配置文件中添加以下代碼後就能夠用了。
'rules' => array( array( 'class' => 'webroot.protected.components.urlRules.CategoryUrlRule', ), '<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>', ),
這裏只是爲了講明自定義UrlRule類的大體寫法。
其實上邊三個步驟中的函數能夠單獨寫到一個父類中,全部本身擴展的UrlRule類都繼承這個父類,而不需每一個自定義的UrlRule類都去重寫那三個函數 。