前两天做了网站SEO方面的URL优化工作。
具体要求是:商品分类的URL中需要有这个分类的汉语拼音出现,
如:http://www.abc.com/category-shafa.html
(之前的URL大致是这样的:http://www.abc.com/category/index/f/24/c/279/p.html,其中f参数对应分类id )
分析:在分类表里面添加拼音字段;在url生成之前,将f参数转换成所需的拼音。在解析URL之前,将拼音转换成对应的分类id。
从哪里开始入手呢,首先想到的是在main.php中的urlManage配置中德rules数组中添加规则,
难题来了,id转换成拼音是需要查数据库的,在main.php中查数据库只能用原生的sql来查询,不但影响系统的扩展,还会破坏配置文件的整洁性。
于是乎,查了一下Yii的API,发现Yii的扩展性做的相当出色,rules参数里面还可以传入数组,而CUrlManager只是递归处理了一下这个rules就解决了数组参数。很佩服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()
站在巨人的肩膀上,你才能前进的更快,确实如此。
现在只需简单几步就可以完成任务:
1、创建一个MyUrlRule类,继承自CUrlRule,(API中说是继承CBaseUrlRule,既然CUrlRule已经继承自CBaseUrlRule,且功能很完善,所以此处直接继承CUrlRule )
2、重写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类,接下来就是:
1、在构造函数中添加rule;
2、在createUrl()中先修改$params(将id转换为pinyin,转换后如果有多余的参数,可以unset掉。);
3、在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类都去重写那三个函数 。