ThinkPHP在PATHINFO的URL模式下,URL的格式类似于http://www.domain.com/appName/module/action
即:http://www.domain.com/分组名/模块名/方法名
或者:http://www.domain.com/模块名/方法名
然而在有些类似于58同城这样的应用中,需要分城市展示不同的页面内容,我们希望在网站域名后面紧跟一个城市目录,也即这种格式:
http://www.domain.com/城市名/模块名/方法名,根据不同的城市展示当地的信息内容
这里大家可能最容易想到也是最暴力的一个方法就是:为每一个城市建立一个单独的分组,然后把相同的代码copy N份......这显然违背了软件设计中代码重用的原则,而且不够灵活,所以我们不提倡。
那么下面我要介绍的是利用Thinkphp的URL路由来实现这一功能:
如果你还不知道怎么配置URL路由的话,建议你先暂停一下,戳下面的链接先了解一下:(只看规则路由部分就行了,先无视正则路由。。)
Thinkphp 3.1.3手册-URL路由部分:http://doc.thinkphp.cn/manual/url_route.html
Thinkphp 3.1快速入门(6)路由:http://www.thinkphp.cn/info/118.html
我先去喝杯茶。。。。
好了欢迎回来,在使用URL路由之前,我们不妨先考虑这样一个问题:如果不考虑城市名在URL中的位置,怎么实现一站多城市呢?很简单,我们可以把城市名作为get参数放置在URL方法名的后面,像这样,http://www.domain.com/模块名/方法名/city/hangzhou,有了这个get参数就好办了,我们可以建立一个CommonAction控制器,其它控制器都继承它,在CommonAction控制器的_initialize()方法中对$_GET['city']做一下判断,可以查询一下数据库,检测是否存在这个城市,如果存在则显示这个城市的内容,为了防止不必要的重复查询数据库,你还可以将城市名保存到cookie里,这个就不多说了。
好了,下面我们就好开始进行URL的乾坤大挪移了,我们的目标是把URL中的模块名和方法名往后移一个“位置”,腾出一个位置来放置城市名,像这样:http://www.domain.com/hangzhou/模块名/方法名
这里我们需要知道,URL中的分组、模块、方法本质上都是get参数,分别为g,m,a(TP3.2版本为m,c,a),知道这个就好办了,我们在config文件中配置URL路由规则如下:
'URL_ROUTE_RULES'=> array(
':city^Home|Admin|Index|Public|User|detail/:m/:a' => 'Home',//city作为get参数,注意这里排除了分组名和控制器名(以及你下面的路由规则),如果一级目录不是城市,即直接为正常路由
//TP3.2版本为':city^-Index-Public-User-detail/:m/:a' => 'Home',
'detail/:id\d$' => 'team/detail',//其它路由规则
),
(补充:3.2.1版本的规则排除分隔符为“-”,把上面代码中的“|”修改为“-”就行了,同时注意把m改成c,下同)
这里的:city,:m,:a都是隐式传入的GET参数,^Home|Admin|Index|Public|User|detail这串内容的作用是排除Home、Admin、Index、Public、User、detail这些关键字,他们都是分组名、模块名或者我们后面定义的路由规则的开头字符串,这样就兼容了正常的URL模式,如果一级目录不是城市,即直接转为正常路由或匹配下面的路由项。
到此为止,我们的路由规则已经基本配置完成了,实现了“http://www.domain.com/城市名/模块名/方法名”这种格式的路由匹配。
但是还有一些小问题,就是默认控制器和默认操作方法的问题,如果我们访问这个URL:“http://www.domain.com/城市名,我们希望访问的完整URL是:“http://www.domain.com/城市名/Index/index,同样,如果我们访问这个URL:“http://www.domain.com/城市名/模块名”,我们希望访问的完整URL是:“http://www.domain.com/城市名/模块名/index”,为了考虑这些情况,我们在原有路由规则之上再加两条路由规则:
'URL_ROUTE_RULES'=> array(
':city^Home|Admin|Index|Public|User|detail$' => 'Home',
':city^Home|Admin|Index|Public|User|detail/:m$' => 'Home',
':city^Home|Admin|Index|Public|User|detail/:m/:a' => 'Home',//city作为get参数,注意这里排除了分组名和控制器名(以及你下面的路由规则),如果一级目录不是城市,即直接为正常路由
'detail/:id\d$' => 'team/detail',//其它路由规则
),
这样就解决了上述问题,当然目前还无法支持“http://www.domain.com/城市名/分组名/模块名/方法名”这样的URL,相信实现原理类似,但会复杂很多。
以上就是一站多城市路由配置技巧。如果你有自己的域名,那么实现一站多城市更fashion的方式莫过于为每个城市部署二级域名了,像这样:http://hangzhou.domain.com/模块名/方法名,下面扯一下怎么为每个城市部署二级域名,基本原理其实是一样的,这里要用到ThinkPHP二级域名部署,详见手册:http://doc.thinkphp.cn/manual/sub_domain_deploy.html
我们在config文件里部署二级域名:
'APP_SUB_DOMAIN_DEPLOY'=>1, // 开启子域名配置
/*子域名配置
*格式如: '子域名'=>array('分组名/[模块名]','var1=a&var2=b');
*/
'APP_SUB_DOMAIN_RULES'=>array(
'beijing'=>array('Home/','city=beijing'), // city作为一个get参数,指明城市,对应beijing.domain.com
'hangzhou'=>array('Home/','city=hangzhou'), // city作为一个get参数,指明城市,对应hangzhou.domain.com
),
就这么简单,而且二级域名部署和之前的URL路由是不冲突的,当然既然部署了二级域名就完全不需要URL路由了。
好了就扯这么多,感谢你阅读完本教程,希望对你的项目有所帮助,有任何问题欢迎留言讨论。
补充:如果你使用的ThinkPHP是3.2.1版本的并且字段排除出现了奇怪的错误,这是因为规则排除和函数功能使用的分隔符相同都为“|”导致混淆,目前TP官方已经修复这一BUG,把排除分割符该为了“-”,所以还请你赶紧更新你的TP版本。3.1.3版本无此问题。