欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

ThinkPHP3.1快速入门(6)路由

shiping1 的头像
ThinkPHP框架对URL有一定的规范,所以如果你希望定制你的URL格式的话,就需要好好了解下内置的路由功能了,它能让你的URL变得更简洁和有文化。

启用路由

要使用路由功能,前提是你的URL支持PATH_INFO,并且在项目配置文件中开启路由:
  1. 'URL_ROUTER_ON'   => true, //开启路由
复制代码
 
然后就是配置路由规则了,使用URL_ROUTE_RULES参数进行配置,配置格式是一个数组,每个元素都代表一个路由规则,例如:
  1. 'URL_ROUTE_RULES'=>array(
  2.     'news/:year/:month/:day' => array('News/archive', 'status=1'),
  3.     'news/:id'               => 'News/read',
  4.     'news/read/:id'          => '/news/:1',
  5.  ),
复制代码
 
系统会按定义的顺序依次匹配路由规则,一旦匹配到的话,就会定位到路由定义中的模块(支持分组)和操作方法去执行,并且后面的规则不会继续匹配。

路由定义

路由规则的定义方式如下:
  1. '路由表达式'=>'路由地址和额外参数'
复制代码
 

路由表达式

路由表达式包括规则路由和正则路由的定义表达式,只能使用字符串。
表达式示例
正则表达式/^blog\/(\d+)$/
规则表达式blog/:id
正则表达式
路由表达式支持的正则定义必须以“/”开头,否则就视为规则表达式。也就是说如果采用
  1. '#^blog\/(\d+)$#'
复制代码
 
方式定义的正则表达式不会被支持,而会被认为是规则表达式进行解析,从而无法正确匹配。
  1. '/^new\/(\d{4})\/(\d{2})$/' => 'News/achive?year=:1&month=:2',
复制代码
 
对于正则表达式中的每个变量(即正则规则中的子模式)部分,如果需要在后面的路由地址中引用,可以采用:1、:2这样的方式,序号就是子模式的序号。
更多的关于如何定义正则表达式就不在本文的描述范畴了。

规则表达式
3.0的规则路由是从2.1的简单路由进化而来,虽然无法完美实现正则路由的功能,但是相比简单路由确实增强了不少,而且比正则路由更方便定义和容易理解。
规则表达式通常包含静态地址和动态地址,或者两种地址的结合,例如下面都属于有效的规则表达式:
  1. 'my'=>'Member/myinfo', // 静态地址路由 类似于之前版本的简单路由
  2.  'blog/:id'=>'Blog/read', // 静态地址和动态地址结合
  3.  'new/:year/:month/:day'=>'News/read', // 静态地址和动态地址结合
  4.  ':user/:blog_id'=>'Blog/read',// 全动态地址
复制代码
 
规则表达式的定义以“/”为参数分割符(无论你的URL_PATHINFO_DEPR设置是什么,请确保在定义规则表达式的时候统一使用“/”进行URL参数分割)。
每个参数中以“:”开头的参数都表示动态参数,并且会自动对应一个GET参数,例如:id表示该处匹配到的参数可以使用$_GET['id']方式获取,:year :month :day 则分别对应$_GET['year'] $_GET['month'] $_GET['day']。

数字约束
支持对变量的类型检测,但仅仅支持数字类型的约束定义,例如
  1. 'blog/:id\d'=>'Blog/read',
复制代码
 
表示只会匹配数字参数,如果你需要更加多的变量类型检测,请使用正则表达式定义来解决。

规则排除
非数字变量支持简单的排除功能,主要是起到避免解析混淆的作用,例如:
  1. 'news/:cate^add|edit|delete'=>'News/category'
复制代码
 
因为规则定义的局限性,恰巧我们的路由规则里面的news和实际的news模块是相同的命名,而:cate并不能自动区分当前URL里面的动态参数是实际的操作名还是路由变量,所以为了避免混淆,我们需要对路由变量cate进行一些排除以帮助我们进行更精确的路由匹配,格式^add|edit|delete表示,匹配除了add edit 和delete之外的所有字符串,我们建议更好的方式还是改进你的路由规则,避免路由规则和模块同名的情况存在,例如
  1. 'new/:cate'=>'News/category'
复制代码
 
就可以更简单的定义路由规则了。

完全匹配
规则匹配检测的时候只是对URL从头开始匹配,只要URL地址包含了定义的路由规则就会匹配成功,如果希望完全匹配,可以使用$符号,例如:
  1. 'new/:cate$'=> 'News/category',
复制代码
 
  1. http://serverName/index.php/new/info
复制代码
 
会匹配成功
  1. http://serverName/index.php/new/info/2 
复制代码
 
则不会匹配成功
如果是采用
  1. 'new/:cate'=> 'News/category',
复制代码
 
方式定义的话,则两种方式的URL访问都可以匹配成功。

路由地址和额外参数

路由地址和额外参数表示前面的路由表达式最终需要路由到的地址并且允许隐式传入URL里面没有的一些参数,这里允许使用字符串或者数组方式定义,支持下面5种方式定义:
定义方式定义格式
方式1:路由到内部地址(字符串)'[分组/模块/操作]?额外参数1=值1&额外参数2=值2...'
方式2:路由到内部地址(数组)参数采用字符串方式array('[分组/模块/操作]','额外参数1=值1&额外参数2=值2...')
方式3:路由到内部地址(数组)参数采用数组方式array('[分组/模块/操作]',array('额外参数1'=>'值1','额外参数2'=>'值2'...))
方式4:路由到外部地址(字符串)301重定向'外部地址'
方式5:路由到外部地址(数组)可以指定重定向代码array('外部地址','重定向代码')
如果路由地址以“/”或者“http”开头则会认为是一个重定向地址或者外部地址,例如:
  1. 'blog/:id'=>'/blog/read/id/:1'
复制代码
 
  1. 'blog/:id'=>'blog/read/'
复制代码
 
虽然都是路由到同一个地址,但是前者采用的是301重定向的方式路由跳转,这种方式的好处是URL可以比较随意(包括可以在URL里面传入更多的非标准格式的参数),而后者只是支持模块和操作地址。举个例子,如果我们希望avatar/123重定向到
/member/avatar/id/123_small的话,只能使用:
  1. 'avatar/:id'=>'/member/avatar/id/:1_small'
复制代码
 
路由地址采用重定向地址的话,如果要引用动态变量,也是采用:1、:2 的方式。

采用重定向到外部地址通常对网站改版后的URL迁移过程非常有用,例如:
  1. 'blog/:id'=>'http://blog.thinkphp.cn/read/:1'
复制代码
 
表示当前网站(可能是http://thinkphp.cn)的 blog/123地址会直接重定向到 http://blog.thinkphp.cn/read/123

在路由跳转的时候支持额外传入参数对(额外参数指的是不在URL里面的参数,隐式传入需要的操作中,有时候能够起到一定的安全防护作用,后面我们会提到),支持“额外参数1=值1&额外参数2=值2”或者array('额外参数1'=>'值1','额外参数2'=>'值2'...)这样的写法,可以参考不同的定义方式选择。例如:
  1. 'blog/:id'=>'blog/read/?status=1&app_id=5',
  2.  'blog/:id'=>array('blog/read/?status=1&app_id=5'),
  3.  'blog/:id'=>array('blog/read/','status=1&app_id=5'),
  4.  'blog/:id'=>array('blog/read/',array('status'=>1,'app_id'=>5)),
复制代码
 
上面的路由规则定义中额外参数的传值方式都是等效的。status和app_id参数都是URL里面不存在的,属于隐式传值,当然并不一定需要用到,只是在需要的时候可以使用。

实例说明

通过上面的讲解,我们了解了如何定义路由规则,下面我们来举个例子加深印象。
假设我们定义了News控制器如下(代码实现仅供参考):
  1. class NewsAction extends Action{
  2.     public function read(){
  3.         $New = M('New');
  4.         if(isset($_GET['id'])) {
  5.             // 根据id查询结果
  6.             $data = $New->find($_GET['id']);
  7.         }elseif(isset($_GET['name'])){
  8.             // 根据name查询结果
  9.             $data = $New->getByName($_GET['name']);
  10.         }
  11.         $this->data = $data;
  12.         $this->display();
  13.     }
  14.  
  15.     public function archive(){
  16.         $New = M('New');
  17.         $year   =   $_GET['year'];
  18.         $month  =   $_GET['month'];
  19.         $begin_time = strtotime($year . $month . "01");
  20.         $end_time = strtotime("+1 month", $begin_time);
  21.         $map['create_time'] =  array(array('gt',$begin_time),array('lt',$end_time));
  22.         $map['status']  =   1;
  23.         $list = $New->where($map)->select();
  24.         $this->list =   $list;
  25.         $this->display();
  26.     }
  27.  }
复制代码
 
定义路由规则如下:
  1. 'URL_ROUTE_RULES' => array( //定义路由规则 
  2.     'new/:id\d'    => 'News/read',
  3.     'new/:name'    => 'News/read',
  4.     'new/:year\d/:month\d'  => 'News/archive',
  5.  ),
复制代码
 
然后,我们访问:
  1. http://serverName/index.php/new/8
复制代码
 
会匹配到第一个路由规则,实际执行的效果等效于访问:
  1. http://serverName/index.php/News/read/id/8
复制代码
 
当访问:
  1. http://serverName/index.php/new/hello
复制代码
 
会匹配到第二个路由规则,实际执行的效果等效于访问:
  1. http://serverName/index.php/News/read/name/hello
复制代码
 
那么如果访问:
  1. http://serverName/index.php/new/2012/03
复制代码
 
是否会匹配第三个路由规则呢?我们期望的实际执行的效果能够等效于访问:
  1. http://serverName/index.php/News/archive/year/2012/month/03
复制代码
 
事实上却没有,因为http://serverName/index.php/new/2012/这个URL在进行路由匹配过程中已经优先匹配到了第一个路由规则了,把2012当成id的值传入了,这种情况属于路由规则的冲突,解决办法有两个:
1、调整定义顺序
路由定义改成:
  1. 'URL_ROUTE_RULES' => array( //定义路由规则
  2.     'new/:year\d/:month\d'  => 'News/archive',
  3.     'new/:id\d'                    => 'News/read',
  4.     'new/:name'    => 'News/read',
  5.  ),
复制代码
 
接下来,当我们再次访问:
  1. http://serverName/index.php/new/2012/03
复制代码
 
的时候,达到了预期的访问效果。所以如果存在可能规则冲突的情况,尽量把规则复杂的规则定义放到前面,确保最复杂的规则可以优先匹配到。但是如果路由规则定义多了之后,仍然很容易混淆,所以需要寻找更好的解决办法。
2、利用完全匹配功能
现在我们来利用路由的完全匹配定义功能,把路由定义改成:
  1. 'URL_ROUTE_RULES' => array( //定义路由规则
  2.     'new/:id\d$'                    => 'News/read',
  3.     'new/:name$'    => 'News/read',
  4.     'new/:year\d/:month\d$'  => 'News/archive',
  5.  ),
复制代码
 
在规则最后加上$符号之后,表示完整匹配当前的路由规则,就可以避免规则定义的冲突了。对于规则路由来说,简单的理解就是URL里面的参数数量或者类型约束要完全一致。
所以,如果我们访问
  1. http://serverName/index.php/new/2012/03/01
复制代码
 
的话,是不会匹配成功任何一条路由的。
3、利用正则路由
当然,解决问题的办法总是不止一种,对于复杂的情况,我们不要忘了使用正则路由规则定义,在你找不到解决方案的时候,正则路由总能帮到你。
要实现上面的同样路由功能的话,还可以用下面的规则定义:
  1. 'URL_ROUTE_RULES' => array( //定义路由规则
  2.     '/^new\/(\d+)$/'        => 'News/read?id=:1',
  3.     '/^new\/(\w+)$/'        => 'News/read?name=:1',
  4.     '/^new\/(\d{4})\/(\d{2})$/' => 'News/achive?year=:1&month=:2',
  5.  ),
复制代码
 
来自  http://www.thinkphp.cn/info/118.html

宇宙西瓜03月30日
噗....有文化是什么鬼啊
回复think311月16日
优雅而有内涵
lylan2014年11月28日
数据查询,页面不显示结果,代码如下
  1.  
  2.  public function read($id=15){
  3.     $Form = M("Form");
  4.     $data = $Form->where('id=17')->select();
  5.     $this->data = $data;
  6.     $this->display();
  7.  }
  8.  
复制代码
 
复制代码
 
复制代码
 
复制代码
 
复制代码
 
复制代码
 
回复lylan2014年11月28日
页面的代码是这样的,显示了id,标题,内容,没有后面的结果,数据库里面是有该条数据的,求助~~~ quickly!! Thanks!!
  1.  
  2.  <table>
  3.         <tr>
  4.             <td>id:</td>
  5.             <td>{$data.id}</td>
  6.         </tr>
  7.         <tr>
  8.             <td>标题:</td>
  9.             <td>{$data.title}</td>
  10.         </tr>
  11.         <tr>
  12.             <td>内容:</td>
  13.             <td>{$data.content}</td>
  14.         </tr>
  15.     </table>
  16.  
复制代码
 
复制代码
 
复制代码
 
复制代码
 
复制代码
 
复制代码
 
回复zhogum103003月25日
回复 lylan : $Form->where('id=17')->find(); 改成find()
lm010972014年10月22日
秒懂
悄悄的我来2014年10月09日
为什么我定义了路由规则之后,新的路由地址和原来的url地址都可以访问?
lyman2014年09月04日
我比较喜欢,这个不做好,做网站SEO优化就没有URL的优势了。
镜花水月2014年06月18日
这个不会自动重写的路由规则定个鸟用啊
qishaobo2014年06月04日
规则写完后,链接地址都改了也可以用,但是浏览器的地址栏怎么还是规则前的url
镜花水月2014年06月02日
tp的路由没有后盾的好 后盾的配置好路由会自动调整url 而这里要手工修改
梅花香2014年03月06日
如果一个项目已经开发好,没有用这个路由,但是现在打算用这个路由规则,那么,写好规则后,TPL里的U方法的写法也需要去重写,那算起来,比较复杂吧。出力不讨好好。以这样的规则来看,就是需要在一开始就制定好,不然该起来不是一般的麻烦
回复微辅导2014年03月12日
不打算用路由,感觉麻烦……
回复iLoveProgram2014年04月11日
回复 微辅导 : 都不难哈。做程序员,先苦后甜,先死后活。
回复hitsword2014年05月24日
如果已经开发好,直接用HttpServer的rewrite就好。何必折腾。
回复kais7mg2014年09月04日
回复 hitsword : 这个thinkphp的路由规则 确实是替代 HttpServer的rewrite, 功能都一样, 但写在 HttpServer 的 每次修改都需要重启服务
回复a844134322014年12月11日
回复 kais7mg : thinkphp的路由规则做不了 泛解析的效果 它不会动态重定向,没什么作用
xuzc2014年02月12日
这叫路由,不正确吧。这就像伪静态的规则一样,正确的叫法:url规则。
一看到路由,绝大多数人以为硬件的路由器。
回复iLoveProgram2014年04月11日
呵呵,幸亏先看到你说了。不然真以为是?一个应用层开发跟硬件毛事。
回复kais7mg2014年09月04日
回复 iLoveProgram : 就是url重写啦 , 功能和 apache的rewrite模块一样
回复jscreat04月14日
回复 kais7mg : 在nginx下,apache的重写规则是不能用的,有这个省事很多
monarch2013年11月19日
太复杂,不够简单
剑出惊雷2013年10月31日
开发者应当借鉴Python哲学,一种问题只提供一种解决方法,且是最优方法,显然最优方法就是对搜索引擎友好的URL
qq176620542013年10月31日
这些都尝试过了。但是实际案例中需要url后台自定义啊。
例如我们整合了栏目功能和文章功能。URL需要达到这个效果:
用户输入 yoursite/catedir可以访问 catedir栏目
用户输入 yousite/catedir/1.html 就是catedir里面id为1的文章
PS:项目里面肯定没有catedirAction存在的,catedir是用户后台添加的,写进数据库的。
类似这种需求咋整啊
madcard2013年10月03日
我倒觉得挺好懂的。。比直接写正则好一点点。
yujianxingfu_m2013年09月27日
简化访问地址。更符合SEO
chyoo2013年09月24日
配置路由有什么作用吗?
回复6098101642013年12月05日
可以是url更加简洁,有时一串复杂的url可以通过简洁的较短的字符串来访问
fjsxrc2013年09月19日
url路由能解决什么问题?用路由改进哪几方面的东西?如果不用可以吗?有哪些缺点?这篇文章云里雾里,太高深了吧。唉有些人就是喜欢简单问题复杂化。一个茴香豆的“茴”字整出四种写法,酸!
TP小斗士2013年09月09日
说的正则很吊的样子,要好好研究研究。。
du1007102603252013年09月03日
一点都看不懂
mcbaby2013年09月02日
相当于URL重写?朕已阅。。
流年凉薄2013年08月27日
没有php基础的看这个好难理解、求讲的更详细点的手册^_^。。。
5646431222013年08月20日
官方可以沒講一個教程都給出一個深刻的例子,這樣更好瞭解//
回复tingyuxuan2013年08月25日
敢问楼主是香港的还是台湾的?居然用的是繁体!!
回复5646431222013年08月28日
回复 tingyuxuan : 廣州的,公司是香港的,跟香港溝通打字必須用繁體!
回复tingyuxuan2013年08月29日
回复 564643122 : 喔
simonyao2013年08月08日
表示有待研究!!
cc28862013年07月26日
感觉就是.htaccess的变种
leec19912013年07月02日
当遇到不理解的概念时,多google,baidu,搜索关键词,多看几个例子就明白了。
混世小子2013年07月02日
希望可以再加一点比较简单的基础点的讲解,初学者确实有点看不懂额
yhhcbin2013年06月17日
URL_ROUTER_ON设置true,然后在URL_ROUTE_RULES设置这句'/^news\/(\d+)\.html$/' => 'News/read?id=:1'后,为什么不能用http://localhost/company/news/1.html访问?
回复翡冷翠的夜2013年08月09日
yhhcbin2013年06月17日
案例有部分实现不了
xianglingchuan2013年06月12日
路由的配置信息我应该写在什么/config/config.php 里面不对,需要放在什么地方?
回复6098101642013年12月05日
放在/config/config.php的return array()括号里面
回复meahu2014年05月09日
回复 609810164 : 是Conf/config.php里
小网蚁2013年06月01日
这个不是入门级的吧!
djjsy2013年04月26日
这个是给有基础的人看的,如果初学者的话,必须要看开发手册才能明白是什么意思。初学者只能是云里雾里了。
回复liujun05112013年08月09日
呵呵!
lin36152013年04月23日
刚刚研究了一会,虽然只弄成功一个,不过原理就是伪静态的变种,其实也变不到那里去!
吖,晚安2013年04月16日
可不可以理解为:类似于伪静态???
wwxx2013年04月16日
url里面有短横线怎么办?实际运用中经常用到啊~比如http://domain/list1-2-3-4.html 这样的url没法路由路由了哇?
回复wwxx2013年04月16日
OH SORRY 正则可以实现,刚才少了一个转义,'/(\w+)\/list\-(\d+)/' => 'Cat/cat', 给遇到相同问题的朋友一个参考吧~
baiwapak2013年02月20日
啥意思啊。云里雾里的
startxc2013年01月07日
方式3:路由到内部地址(数组)参数采用数组方式,试了很多次都不行。
'blog/:id'=>'blog/read/',这个是错的,要把最后那个横杠去掉才行,改成这样:'blog/:id'=>'blog/read'
startxc2013年01月07日
[b]URL_ROUTER_ON的参数在哪里呢[/b]
qiaoyinqian2012年12月14日
这个确实看不懂,
baoying19899202012年12月06日
路由配置了怎么不对啊
power7212012年12月06日
$map['create_time'] = array(array('gt',$begin_time),array('lt',$end_time));
应该是
$map['create_time'] = array(array('egt',$begin_time),array('lt',$end_time));
吧?
不过也没多大影响O(∩_∩)O哈哈~
henhao1632012年11月20日
困了两天了,url中存在中文获取不到?
XO9001-BX2012年11月16日
重定向后模板找不到。什么情况?
csmoumou2012年11月07日
不错,顶一个
happysww12302012年11月07日
路由功能是干什么用的?能否发个具体的项目中的实例?看了不太明白
wszdw1422012年10月23日
看到正则就头疼!
evasnowind2012年10月21日
能否提供一下数据库表啊,想自己跑一下,但没有数据,只能大概猜出表里有啥……
thinkphp2012年10月11日
一个好的框架,既要做到入门容易,也要能够实现精通难的境界。就拿路由功能来说,如果只提供一个正则路由功能,看起来是统一了,但是很多开发人员对正则是很头疼的,不适合新手。而TP推出的规则路由则提供了更简洁易懂的方式来定义。
回复极速在线2012年10月17日
同意!
linwu2012年10月11日
既然想做快速开发框架,为什么不遵从“simple is best ”理念,一个功能尽可能只有一种实现方式。比如这里,只提供一个正则路由就可以解决所有路由问题。
回复petrie2012年12月06日
快读开发 不代表 快速学习
weir19902012年10月06日
不粗,很受用。。
wintrue2012年09月28日
很好用,非常强大,好强大啊,用了路由,用户中心可以绑定独立域名了
feng887242012年09月28日
跟Django很相似
xieph2012年09月26日
有些看不懂啊,应该提供点案例~
回复xieph2012年09月26日
在示例那找到路由的代码了,研究下
回复feng887242012年09月28日
跟Django很相似
aoyoo2012年09月08日
太难了
回复lyongde2012年09月14日
实操一下,挺简单的。

普通分类: