[TOC]
路由功能 [ThinkPHP6.0 開發手冊](http://www.hmoore.net/manual/thinkphp6_0) 做了詳細的介紹,并且官方還出了一本教程 [完全指南](http://www.hmoore.net/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. 我們用命令行工具查看一下定義的路由列表

我們在終端執行 `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地址結果:

從上圖中,我們可以看到使用路由規則(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(), // 資源推薦
]);
}
.
.
.
```
否則,會報以下錯誤:

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] 以上是筆者發現的幾個問題,大家如果對這些問題有好的解決方案,請不吝賜教。另外,大家也可以嘗試用[增加應用入口](http://www.hmoore.net/manual/thinkphp6_0/1297876)想去除URL里的應用名。
- 第一章 基礎信息
- 序言
- 關于作者
- PHP和ThinkPHP
- 如何正確閱讀本書
- 寫作約定
- 開發規范
- 章節體例
- 本書源碼
- 第二章 舞臺布置
- 開發環境
- 產品分解
- Git和GitHub
- 創建項目
- 數據庫視圖管理工具
- 統一代碼風格
- 目錄結構
- 配置信息
- 后臺應用搭建
- 助手函數
- 前臺布局模板
- 基礎控制器
- 小結
- 第三章 注冊登錄
- 數據遷移
- 表單提交
- 表單驗證
- 模型驗證
- 短信提供商
- 發送短信
- 手機驗證
- 注冊提醒
- 登錄與退出
- 重置密碼
- 數據填充
- 小結
- 第四章 用戶相關
- 個人中心
- 編輯個人資料
- 上傳圖片
- 上傳頭像
- 顯示頭像
- 限制頭像分辨率
- 裁剪頭像
- 顯示注冊時間
- 授權訪問
- 小結
- 第五章 帖子列表
- 話題分類
- 話題模型
- 話題列表
- 性能優化
- 分類話題列表
- 話題列表排序
- 用戶發布的話題
- 分頁器美化
- 小結
- 第六章_帖子CURD
- 創建話題
- 生成摘要
- 編輯器優化
- 上傳圖片
- 顯示話題
- 編輯話題
- 刪除話題
- 小結
- 第七章 帖子回復
- 回復模型
- 回復列表
- 發表回復
- 刪除回復
- XSS 安全漏洞
- 小結
- 第八章 角色權限和管理后臺
- 多角色用戶權限
- 用戶管理
- 話題管理
- 回復管理
- 小結
- 第九章 雜項
- 邊欄活躍用戶
- 用戶最后登錄時間
- 邊欄資源推薦
- 站點首頁
- 小結
- 第十章 總結
- 全書總結
- 附錄
- 淺談ThinkPHP6.0 路由