illuminatech / model-route

允许在绑定的模型不存在的情况下继续路由匹配

1.0.5 2024-03-25 11:05 UTC

This package is auto-updated.

Last update: 2024-08-25 12:09:48 UTC


README

真正的Laravel模型路由匹配


此扩展允许在绑定的模型不存在的情况下继续路由匹配。

有关许可证信息,请查看LICENSE文件。

Latest Stable Version Total Downloads Build Status

安装

安装此扩展的首选方法是通过composer

运行

php composer.phar require --prefer-dist illuminatech/model-route

或将其添加到您的composer.json文件的要求部分。

"illuminatech/model-route": "*"

使用方法

此扩展允许在绑定的模型不存在的情况下继续路由匹配。

假设我们需要创建类似于GitHub的URL结构。这里有单独的用户和组织,每个用户和组织都有自己的页面,其URL从其名称开始。

很可能,在您的项目中,用户和组织将存储在不同的数据库表中,因此此情况的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`

不幸的是,您无法总是控制所有路由定义的顺序。一些包(例如 TelescopeHorizonNova)通过单独的服务提供程序注册自己的路由。这些路由可能会在我们的 "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" 路由周围的数据库查询。