illuminatech / model-route
允许在绑定的模型不存在的情况下继续路由匹配
Requires
- illuminate/routing: ^5.8 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0
Requires (Dev)
- illuminate/database: *
- illuminate/events: *
- phpunit/phpunit: ^7.5 || ^8.0 || ^9.3 || ^10.5
README
真正的Laravel模型路由匹配
此扩展允许在绑定的模型不存在的情况下继续路由匹配。
有关许可证信息,请查看LICENSE文件。
安装
安装此扩展的首选方法是通过composer。
运行
php composer.phar require --prefer-dist illuminatech/model-route
或将其添加到您的composer.json文件的要求部分。
"illuminatech/model-route": "*"
使用方法
此扩展允许在绑定的模型不存在的情况下继续路由匹配。
假设我们需要创建类似于GitHub的URL结构。这里有单独的用户和组织,每个用户和组织都有自己的页面,其URL从其名称开始。
- https://github.com/klimov-paul - 用户页面,其中 "klimov-paul" 是用户名称。
- https://github.com/illuminatech - 组织页面,其中 "illuminatech" 是组织名称。
很可能,在您的项目中,用户和组织将存储在不同的数据库表中,因此此情况的Laravel路由配置将如下所示。
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Route; Route::get('{user}', UserController::class.'@show')->name('users.show'); Route::get('{organization}', OrganizationController::class.'@show')->name('organizations.show');
控制器代码将如下所示。
<?php namespace App\Http\Controllers; use App\Models\User; class UserController extends Controller { public function show(User $user) { // ... } } use App\Models\Organization; class OrganizationController extends Controller { public function show(Organization $organization) { // ... } }
但是,这不会正常工作。问题在于第二个路由("organizations.show")永远不会被匹配,因为任何组织名称都只会与第一个路由("users.show")进行比较,在尝试访问组织页面时会触发404错误。
此扩展通过额外的URL匹配验证器 - \Illuminatech\ModelRoute\ModelRouteValidator
解决了此问题。注册后,它将特定模型的存续作为路由匹配条件。这允许在当前路由绑定的模型不存在时传递匹配到下一个路由。最佳注册位置将是您的路由服务提供商。例如
<?php namespace App\Providers; use Illuminatech\ModelRoute\ModelRouteValidator; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; class RouteServiceProvider extends ServiceProvider { public function boot() { (new ModelRouteValidator) ->setBinders([ 'user' => \App\Models\User::class.'@username', 'organization' => \App\Models\Organization::class.'@name', ]) ->register(); parent::boot(); } // ... }
设置后,上述路由将被正确解析。如果没有用户记录与请求的URL路由 'users.show' 匹配,则将视为 '未匹配',并且路由将继续到 'organizations.show'。
\Illuminatech\ModelRoute\ModelRouteValidator
允许以类似标准显式绑定的方式设置路由参数绑定。通过 \Illuminatech\ModelRoute\ModelRouteValidator::setBinders()
设置绑定器,它是一个数组,其键是路由参数名称,值是绑定器规范。每个绑定器可以指定为
-
字符串,Eloquent模型类名,例如:'App\Models\User';在这种情况下,参数绑定将在该类及其路由键字段中搜索。
-
字符串,Eloquent模型类名和以
@
符号分隔的搜索字段的对,例如:'App\Models\Item@slug';在这种情况下,参数绑定将在指定的模型及其指定的字段中搜索。 -
可调用对象,PHP回调,它应接受参数的原始值并返回对其的绑定;如果没有找到绑定,则应返回
null
。
例如
<?php use Illuminatech\ModelRoute\ModelRouteValidator; (new ModelRouteValidator) ->setBinders([ 'blog' => \App\Models\BlogPost::class, // search using `\App\Models\BlogPost::getRouteKeyName()` 'item' => \App\Models\Item::class.'@slug', // search using `\App\Models\Item::$slug` 'project' => function ($value) { return \App\Models\Project::query()->where('name', $value)->first(); // if not found - `null` will be returned }, ]) ->register();
注意:对于由
\Illuminatech\ModelRoute\ModelRouteValidator::setBinders()
覆盖的参数,不要指定标准显式路由参数绑定,因为这会导致额外的数据库查询。参数绑定将由\Illuminatech\ModelRoute\ModelRouteValidator
自动设置。
性能调优
请记住,在编写带有模型绑定的路由之前,您应该为任何静态页面指定路由。虽然此扩展允许进行路由匹配,但如果不存在绑定,则匹配检查将带来数据库查询的成本。因此,在我们的“GitHub”示例中,应该事先描述任何预定义站点部分的路由,例如静态页面、联系页面或博客。
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Route; // predefined site sections should be described beforehand: Route::view('about', 'pages/about')->name('about'); Route::view('privacy-policy', 'pages/privacy-policy')->name('privacy-policy'); Route::get('blog', BlogController::class.'@index')->name('blog.index'); Route::get('blog/{blogArticle}', BlogController::class.'@show')->name('blog.show'); // only once all other routes are defined, we can use dynamic binding: Route::get('{user}', UserController::class.'@show')->name('users.show'); // matching check will cause a DB query against model `App\Models\User` Route::get('{organization}', OrganizationController::class.'@show')->name('organizations.show'); // matching check will cause a DB query against model `App\Models\Organization`
不幸的是,您无法总是控制所有路由定义的顺序。一些包(例如 Telescope、Horizon 和 Nova)通过单独的服务提供程序注册自己的路由。这些路由可能会在我们的 "users.show" 和 "organizations.show" 路由之后注册。您可以使用 \Illuminatech\ModelRoute\ModelRouteValidator::setIgnoredUrlPaths()
手动排除特定的URL路径进行匹配。例如
<?php use Illuminatech\ModelRoute\ModelRouteValidator; (new ModelRouteValidator) ->setBinders([ 'user' => \App\Models\User::class.'@username', 'organization' => \App\Models\Organization::class.'@name', ]) ->setIgnoredUrlPaths([ config('telescope.path'), // exclude Telescope URLs config('horizon.path'), // exclude Horizon URLs config('nova.path'), // exclude Nova URLs 'nova-api', // exclude Nova API URLs ]) ->register();
在这种配置下,从 '/telescope'、'/horizon' 或 '/nova' 开始的URL解析将永远不会触发 "users.show" 和 "organizations.show" 路由周围的数据库查询。