[TOC]
路由功能 [ThinkPHP6.0 开发手册](https://www.kancloud.cn/manual/thinkphp6_0) 做了详细的介绍,并且官方还出了一本教程 [完全指南](https://www.kancloud.cn/thinkphp/route-master) (虽然指南是介绍 ThinkPHP5 的,但其中大部分功能也适用于 6.0 ) 。
所以,本文不想过多重复介绍官方文档里的内容,只是简单介绍一些笔者在本书开发过程中遇到的问题或解决方法。
## 为什么在教程里我们优先使用标识路由获取 URL 而不是用资源路由?
下面,我们通过一个完整的 Demo 来演示说明一下这个问题。
1. 把前台应用的路由定义修改成以下内容:
*route/index/app.php*
```php
<?php
use think\facade\Route;
// 话题管理
Route::get('blog/<id>', 'topic/read')->name('blog.read');
Route::get('blog', 'topic/index')->name('blog.index');
// RESTFul 资源路由
Route::resource('article', 'Topic')->only(['index', 'read']);
```
我们在上面定义的这些路由目的是想实现用 `index/blog.html` 和 `index/article.html` 这两个 URL 地址访问话题列表页,并且希望话题详情页的 URL 地址格式是 `index/blog/<id>.html` 或 `index/article/<id>.html` 。另外,我们在上面并没有定义除 `index` 和 `read` 方法以外的控制方法访问路由,所以也不希望它们能被访问。
2. 我们用命令行工具查看一下定义的路由列表
![Routes List](http://tbs.zhanghong.info/images/chapters/11/010_route_list.png)
我们在终端执行 `php think route:list index` (参数 `index` 的意思是指列出 `route/index` 目录内定义的路由列表),可以看到上图结果。从上图中我们可以发现以下两点:
- 使用 `resource` 方法声明时我们限定只定义 `index` 和 `read` 两个控制方法,但最终七个控制方法都定义了;
- 使用 `resource` 方法声明的路由标识(Name)格式是 `控制器名/操作方法名` ,和定义规则(Rule)无关。
3. 我们用 `url` 函数来生成声明的四个路由访问 URL
我们把 `Index` 控制器改成以下代码:
*app/index/controller/Index.php*
```php
<?php
declare (strict_types = 1);
namespace app\index\controller;
class Index extends Base
{
public function index()
{
$list = [
[
'path' => 'name', // 生成URL方式-Name
'name' => 'blog.index', // Name String
'url' => (string) url('[blog.index]'),
'match' => 'index/blog.html', // 预期生成URL地址
], [
'path' => 'name', // 生成URL方式-Name
'name' => 'User/index', // Name String
'url' => (string) url('[User/index]'),
'match' => 'index/member.html', // 预期生成URL地址
], [
'path' => 'path', // 生成URL方式-Rule
'name' => 'member/index', // Rule String
'url' => (string) url('member/index'),
'match' => 'index/member.html', // 预期生成URL地址
], [
'mode' => 'name',
'name' => 'blog.read',
'url' => (string) url('[blog.read]', ['id' => 1]),
'match' => 'index/blog/1.html',
], [
'mode' => 'name',
'name' => 'User/read',
'url' => (string) url('[User/read]', ['id' => 1]),
'match' => 'index/member/1.html',
], [
'path' => 'path',
'name' => 'member/read',
'url' => (string) url('member/read', ['id' => 1]),
'match' => 'index/member/1.html',
],
];
return json($list);
}
}
```
以下是生成的URL地址结果:
![URL Matches](http://tbs.zhanghong.info/images/chapters/11/010_route_list.png)
从上图中,我们可以看到使用路由规则(Rule)或资源路由标识生成的URL地址 **有Bug** ,所以在本项目里我们用自定义标识路由方式生成页面访问URL地址。
>[info] 当使用路由标识生成 URL 时,ThinkPHP 框架允许给标识名前后添加上 `[]`,所以 `redirect('[page.root]')` 和 `redirect('page.root')` 这两种写法效果是等同的( [详见源码](https://github.com/top-think/framework/blob/6.0/src/think/route/Url.php#L356) )。所以,我们为了区分在生成 URL 时使用的是 **规则表达式** 还是 **路由标识** 才给所有路由标识名添加上 `[]` 。
## 把前台应用中件间注册也改成路由中间件
1. 修改渲染模板路径
要把前台中间件改成路由中间件注册时,首先 **必须** 把控制方法(action)的渲染模板名改成 `控制器名/控制方法名` 格式,如以下示例:
*app/index/controller/Topic.php*
```php
<?php
.
.
.
public function index()
{
$param = $this->request->only(['order'], 'get');
return $this->fetch('topic/index', [ // 把 'index' 改成 'topic/index'
'paginate' => TopicModel::minePaginate($param),
'active_users' => UserModel::getActiveUsers(),
'links' => LinkModel::selectAll(), // 资源推荐
]);
}
.
.
.
```
否则,会报以下错误:
![Not Found View Page](http://tbs.zhanghong.info/images/chapters/11/010_no_index_template.png)
2. 修改路由定义文件
在这里,我们还是拿部分路由定义做为 Demo 。
*route/index/app.php*
```php
<?php
use think\facade\Route;
Route::group(function(){
// 话题管理
Route::get('topic/create', 'topic/create')->name('topic.create');
Route::post('topic', 'topic/save')->name('topic.save');
Route::get('topic/<id>/edit', 'topic/edit')->name('topic.edit');
Route::put('topic/<id>', 'topic/update')->name('topic.update');
Route::delete('topic/<id>', 'topic/delete')->name('topic.delete');
})->middleware(['auth']);
Route::group(function(){
Route::get('/', 'topic/index')->name('page.root');
// 用户登录与退出
Route::get('login', 'login/create')->name('page.login');
Route::post('login', 'login/save')->name('page.login.save');
Route::post('logout', 'login/delete')->name('page.logout');
// 话题管理
Route::get('topic/<id>', 'topic/read')->name('topic.read');
Route::get('topic', 'topic/index')->name('topic.index');
Route::get('category/<id>', 'category/read')->name('category.read');
});
```
注意事项:
- 带中间件的路由组 **必须** 定义在前面;
- 不带中间件的路由组的 `group` 方法不能去掉,否则带中间件的路由组不会优先匹配。
## 怎样去掉前台应用所有URL里的应用名前辍(/index)
在本节里,我们把话题列表的 URL 地址由 `http://bbs.test/index/topic.html` 改成 `http://bbs.test/topic.html` 的实现方法,由于修改的代码比较多,我们在这里列出修改内容清单,详细代码请查看 [Git Commit](https://github.com/zhanghong/thinkbbs/commit/d534517ec7da999307a2baf328df1604ccc81c76) 。
- 把 `app/index/config/middleware.php` 注册的中间件移到 `config/middleware.php` ;
- 把 `route/index/app.php` 文件里定义的路由全部迁移到 `route/app.php` 文件;
- 把 `config/view.php` 文件里的模板目录名(view_dir_name)改成`index/view`;
- 把 `app/index/common.php` 文件里定义的助手函数全部移到 `app/common.php` 文件;
- 修改 首页(`index/index`)操作方法代码;
- 把前台应用其它控制方法(action)的渲染模板名`控制器名/控制方法名`。
经过以上步骤,我们采用非 **增加应用入口** 方式实现去掉前台URL里的应用名( `/index` ),但这样实现有一个缺点是默认首页( `/` )必须跳转到话题列表页(`/topic.html`),笔者在实现过程中也尝试了下面两种方法,但都有问题。
1) 把首页操作方法改成以下代码,这样必须在方法内把视图模板目录重新设置成 view 目录( `"view_dir_name"=>'view'` ),另外这样生成的渲染页面里的 URL 地址都不正确;
*app/index/controller/Index.php*
```php
<?php
declare (strict_types = 1);
namespace app\index\controller;
use app\common\model\Link as LinkModel;
use app\common\model\User as UserModel;
use app\common\model\Topic as TopicModel;
class Index extends Base
{
public function index()
{
// 把视图目录重新设置成view
$view_config = [
'view_dir_name' => 'view',
];
$this->app->config->set($view_config, 'view');
$param = $this->request->only(['order'], 'get');
return $this->fetch('topic/index', [
'paginate' => TopicModel::minePaginate($param),
'active_users' => UserModel::getActiveUsers(),
'links' => LinkModel::selectAll(), // 资源推荐
]);
}
}
```
2) 把应用的默认控制器修改成话题控制器( `'default_controller'=>'Topic'` ),修改后 `Topic/index` 方法的代码必须修改成和方法一相同的代码,但还是存在渲染页面里的URL地址都不正确;
>[info] 以上是笔者发现的几个问题,大家如果对这些问题有好的解决方案,请不吝赐教。另外,大家也可以尝试用[增加应用入口](https://www.kancloud.cn/manual/thinkphp6_0/1297876)想去除URL里的应用名。
- 第一章 基础信息
- 序言
- 关于作者
- PHP和ThinkPHP
- 如何正确阅读本书
- 写作约定
- 开发规范
- 章节体例
- 本书源码
- 第二章 舞台布置
- 开发环境
- 产品分解
- Git和GitHub
- 创建项目
- 数据库视图管理工具
- 统一代码风格
- 目录结构
- 配置信息
- 后台应用搭建
- 助手函数
- 前台布局模板
- 基础控制器
- 小结
- 第三章 注册登录
- 数据迁移
- 表单提交
- 表单验证
- 模型验证
- 短信提供商
- 发送短信
- 手机验证
- 注册提醒
- 登录与退出
- 重置密码
- 数据填充
- 小结
- 第四章 用户相关
- 个人中心
- 编辑个人资料
- 上传图片
- 上传头像
- 显示头像
- 限制头像分辨率
- 裁剪头像
- 显示注册时间
- 授权访问
- 小结
- 第五章 帖子列表
- 话题分类
- 话题模型
- 话题列表
- 性能优化
- 分类话题列表
- 话题列表排序
- 用户发布的话题
- 分页器美化
- 小结
- 第六章_帖子CURD
- 创建话题
- 生成摘要
- 编辑器优化
- 上传图片
- 显示话题
- 编辑话题
- 删除话题
- 小结
- 第七章 帖子回复
- 回复模型
- 回复列表
- 发表回复
- 删除回复
- XSS 安全漏洞
- 小结
- 第八章 角色权限和管理后台
- 多角色用户权限
- 用户管理
- 话题管理
- 回复管理
- 小结
- 第九章 杂项
- 边栏活跃用户
- 用户最后登录时间
- 边栏资源推荐
- 站点首页
- 小结
- 第十章 总结
- 全书总结
- 附录
- 浅谈ThinkPHP6.0 路由