Laravel优雅的分割管理路由文件(最佳分割方式)
适用于Laravel >= 5.3。
有两种方式实现路由分割,一种是扩展实现,一种是手动修改代码。推荐使用扩展,因为更为优雅且开箱即用。
第一种方式:使用扩展包
扩展包地址:
GitHub:https://github.com/ixianming/laravel-route-service-provider
国内码云:https://gitee.com/ixianming/laravel-route-service-provider
安装:
composer require ixianming/laravel-route-service-provider
使用包自动发现
-
Laravel 5.5+ 使用包自动发现,所以不需要手动添加
ServiceProvider
。 -
不需要注释 Laravel 的路由服务提供者(除非扩展包抛出提示)。
不使用包自动发现
如果 Laravel 版本小于 5.5 或者不使用包自动发现:
-
将扩展包的服务提供者
Ixianming\Routing\RouteServiceProvider::class
添加到config/app.php
中的providers
数组中。 -
在
config/app.php
中的providers
数组中注释 Laravel 的路由服务提供者App\Providers\RouteServiceProvider::class
。
'providers' => [
// ...
// App\Providers\RouteServiceProvider::class,
Ixianming\Routing\RouteServiceProvider::class,
]
注意:
卸载扩展包后,记得在
config/app.php
中的providers
数组里移除Ixianming\Routing\RouteServiceProvider::class
并取消App\Providers\RouteServiceProvider::class
的注释。
关于扩展包
扩展包继承了 Laravel 的 App\Providers\RouteServiceProvider
。因此,安装扩展包后,依旧可以在 App\Providers\RouteServiceProvider
中的 boot()
方法中定义的路由模型的显式绑定、过滤器、自定义解析逻辑等。但需要注意的是,对 App\Providers\RouteServiceProvider
中 map()
,mapApiRoutes()
,mapWebRoutes()
这三个方法的修改是无效的,因为扩展包覆写了 map()
方法且 map()
方法中不再引用 mapApiRoutes()
,mapWebRoutes()
这两个方法。
扩展包功能
-
扩展包会获取设置的基础中间件组,然后使用默认规则或自定义规则为每个基础中间件组自动分配对应的路由文件并执行加载。
-
可以为同一基础中间件组创建多个路由文件,并且可以将这些路由文件放在
routes
目录下的任何一个地方。开发者可以使用默认匹配规则,也可以自定义各个基础中间件组与路由文件的匹配规则。 -
可以设置全局的异常信息的响应格式为 Json 格式,也可以独立设置每个基础中间件组的异常信息的响应格式是否为 Json 格式。
-
可以自定义每个基础中间件组使用的
domain
。 -
可以自定义每个基础中间件组使用的
prefix
。 -
可以自定义每个基础中间件组的路由名称前缀。
-
在全部路由中检查是否存在重复的 URL(重复的 URL 是指包含域名的完整的 URL),如果存在,将抛出错误信息。扩展包还会指出重复的 URL 所在的路由文件地址,或是指出重复的 URL 所属的基础中间件组,以方便快速定位问题。
-
在全部路由中检查是否存在同名的命名路由,如果存在,将抛出错误信息。扩展包还会指出重复的命名路由所在的路由文件地址,或是指出重复的命名路由所属的基础中间件组,以方便快速定位问题。
注:基础中间件组是指允许自动匹配路由文件的中间件组。
提醒:扩展包不会检查控制器是否重复使用。所以,同安装本扩展包前一样,如果你重复使用了控制器,那么使用
action()
方法生成的控制器的 URL 可能就不是你想要的 URL。
如何自定义配置扩展包,以及更为详细的介绍请前往扩展包地址查看,就不在这里赘述了。
扩展包地址:
GitHub:https://github.com/ixianming/laravel-route-service-provider
国内码云:https://gitee.com/ixianming/laravel-route-service-provider
第二种方式:手动修改代码
在Laravel 5.6/5.5/5.4/5.3版本中,路由文件默认放在了 routes 目录下。
在官方的定义里,http路由有 2 个文件
- 一个是 routes 目录下的 web.php 文件, 它定义了web界面的路由,被分配了web中间件组,以提供session和csrf防护等功能
- 另一个是 routes 目录下的 api.php 文件,文件中的路由则是无状态的,被分配了 api 中间件组。
(关于路由的更多解释请自行参见Laravel文档的路由部分)
无论是使用web中间件的路由还是api中间件的路由,随着项目越来越大,需要的定义的路由越来越多,如果几百上千个路由都定义在一个文件中,我相信将很难进行维护和管理。
因此,为了更方便的管理路由,我们需要对路由进行分割,模块化管理。
接下来我们细细道来:
app/Providers/RouteServiceProvider.php
这个文件负责处理 routes 目录下的所有 http 路由。
源代码片段:
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
//
}
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
从代码中可以看出,map()
方法是实际搜集全部路由的方法,为了代码职责/结构更加清晰,从而定义了 mapApiRoutes()
和 mapApiRoutes()
两个具体的方法,并在 map()
中引用。
换句话说,可以在 app/Providers/RouteServiceProvider.php
文件中新增方法去定义自己的路由!
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
$this->myRoutes();//自定义路由方法
}
protected function myRoutes()//自定义路由方法
{
Route::prefix('xxx')//前缀
->middleware('xxx')//中间件
->namespace($this->namespace)
->group('path');//path为自定义路由文件绝对路径
}
......
可以料想到,如果路由文件很多的话,就得定义很多很多方法了,那上面这个方法就显得相当臃肿了! (╯﹏╰)
下面介绍一种优雅的方式~
我们需要对 app/Providers/RouteServiceProvider.php
这个文件动一下手脚。
-
首先,在文件
app/Providers/RouteServiceProvider.php
中的RouteServiceProvider
类里面添加一个方法,姑且命名为loadRoutesFile($path)
, 这个方法的作用是递归遍历 routes 目录下的全部文件:protected function loadRoutesFile($path){ $allRoutesFilePath = array(); foreach(glob($path) as $file){ if(is_dir($file)){ $allRoutesFilePath = array_merge($allRoutesFilePath, $this->loadRoutesFile($file.'/*')); }else{ $allRoutesFilePath[] = $file; } } return $allRoutesFilePath; }
-
修改
map()
方法为如下public function map() { $allRoutesFilePath = $this->loadRoutesFile(base_path('routes')); $this->mapApiRoutes($allRoutesFilePath); $this->mapWebRoutes($allRoutesFilePath); // }
-
接着修改
mapWebRoutes()
和mapApiRoutes()
两个方法如下-
mapWebRoutes()
方法protected function mapWebRoutes($allRoutesFilePath) { foreach ($allRoutesFilePath as $routesFilePath){ if(ends_with($routesFilePath, 'web.php')){//匹配需要分配web中间的文件 Route::middleware('web') ->namespace($this->namespace) ->group($routesFilePath); } } }
web中间件会被分配到 routes 目录下所有已
web.php
结尾的文件。无论你如何命名,只要路径结尾包含web.php
就会被分配到web中间件。当然,这个匹配规则也可以根据你的自身需求修改。在if
判断语句处修改你的匹配规则即可。例如:
routes |-- web.php |-- api.php |-- channels.php |-- console.php |-- welcome_web.php |-- Userweb.php |-- Role |-- role_web.php |-- roleUserweb.php
web.php
、welcome_web.php
、Userweb.php
、Role/role_web.php
、Role/roleUserweb.php
这几个路由文件会被分配 web中间件。 -
mapApiRoutes()
方法protected function mapApiRoutes($allRoutesFilePath) { foreach ($allRoutesFilePath as $routesFilePath){ if(ends_with($routesFilePath, 'api.php')){//匹配需要分配api中间的文件 Route::prefix('api') ->middleware('api') ->namespace($this->namespace) ->group($routesFilePath); } } }
api中间件会被分配到 routes 目录下所有已
api.php
结尾的文件。无论你如何命名,只要路径结尾包含api.php
就会被分配到api中间件。当然,这个匹配规则也可以根据你的自身需求修改。在if
判断语句处修改你的匹配规则即可。例如:
routes |-- web.php |-- api.php |-- channels.php |-- console.php |-- welcome_api.php |-- Userapi.php |-- Role |-- role_api.php |-- roleUserapi.php
api.php
、welcome_api.php
、Userapi.php
、Role/role_api.php
、Role/roleUserapi.php
这几个路由文件会被分配 api中间件。
-
最后的最后~ 在生产环境上。通过 Laravel 的命令:
php artisan route:cache
上面这个命令会生成 bootstrap/cache/routes.php
文件。
需要注意的是,路由缓存并不会作用在基于闭包的路由。要使用路由缓存,必须将所有闭包路由转换为控制器类。
使用闭包路由,在生成缓存时,将报错!
生成路由缓存文件后,路由就只会读取缓存文件的路由规则啦!不必担心引用多个文件带来的IO性能影响等等。
另外如果添加了任何新的路由,都必须生成新的路由缓存噢!
如果想要移除缓存路由文件的话,可以使用命令:
php artisan route:clear