mmghv/lumen-route-binding

Lumen 路由模型绑定

1.5.5 2024-09-16 18:14 UTC

This package is auto-updated.

Last update: 2024-09-16 18:17:49 UTC


README

Build Status Lumen Version Latest Stable Version Total Downloads Latest Unstable Version License

此包为 Lumen(5 - 11)添加了 Route Model Binding 支持。

众所周知,Lumen 由于不使用 Laravel 使用的 Illuminate 路由,而是使用 FastRoute(速度快得多),因此默认不支持 Route Model Binding。使用此包,我们添加了对强大的 Route Model Binding 的支持,同时仍然能够利用 FastRoute 在 Lumen 中的速度优势。

目录

安装

使用 composer

composer require mmghv/lumen-route-binding "^1.0"

它需要

php >= 7.1
Lumen 5 - 11

注册服务提供者

在下一节 ..

使用

路由模型绑定提供了一种方便的方法,可以将模型实例自动注入到路由中。例如,您可以直接注入匹配给定 ID 的整个 User 模型实例,而不是注入用户 ID。

在哪里定义我们的绑定

创建一个扩展包的提供者并将其放在 app/Providers

// app/Providers/RouteBindingServiceProvider.php

namespace App\Providers;

use mmghv\LumenRouteBinding\RouteBindingServiceProvider as BaseServiceProvider;

class RouteBindingServiceProvider extends BaseServiceProvider
{
    /**
     * Boot the service provider
     */
    public function boot()
    {
        // The binder instance
        $binder = $this->binder;

        // Here we define our bindings
    }
}

然后在 bootstrap/app.php 中注册它

$app->register(App\Providers\RouteBindingServiceProvider::class);

现在我们可以在 boot 方法中定义我们的 bindings

定义绑定

我们有 三种 类型的绑定

1) 显式绑定

我们可以使用 bind 方法显式地将路由通配符名称绑定到特定模型

$binder->bind('user', 'App\User');

这样,在我们的路由中的任何地方,如果找到通配符 {user},它将被解析为与通配符值对应的 User 模型实例,因此我们可以像这样定义我们的路由

$router->get('profile/{user}', function(App\User $user) {
    //
});

幕后,绑定器将按如下方式解析模型实例

$instance = new App\User;
return $instance->where($instance->getRouteKeyName(), $value)->firstOrFail();
自定义键名

默认情况下,它将使用模型的 ID 列。类似于 Laravel,如果您希望它在检索特定模型类时使用其他列,您可以在 Eloquent 模型上重写 getRouteKeyName 方法

/**
 * Get the route key for the model.
 *
 * @return string
 */
public function getRouteKeyName()
{
    return 'slug';
}
使用自定义解析回调

如果您希望使用自己的解析逻辑,您可以将 Class@method 可调用样式或 Closure 传递给 bind 方法,而不是传递类名,可调用将接收 URI 段的值,并应返回应注入到路由中的类的实例

// Using a 'Class@method' callable style
$binder->bind('article', 'App\Article@findForRoute');

// Using a closure
$binder->bind('article', function($value) {
    return \App\Article::where('slug', $value)->firstOrFail();
});
处理 NotFound 异常

如果找不到具有给定键的模型,Eloquent 的 firstOrFail 将抛出 ModelNotFoundException。为了处理此异常,我们可以将闭包作为 bind 方法的第三个参数传递

$binder->bind('article', 'App\Article', function($e) {
    // We can return a default value if the model not found :
    return new \App\Article();

    // Or we can throw another exception for example :
    throw new \NotFoundHttpException;
});

2) 隐式绑定

使用 implicitBind 方法,我们可以告诉绑定器自动绑定给定命名空间中的所有模型

$binder->implicitBind('App');

因此,在这个例子中

$router->get('articles/{article}', function($myArticle) {
    //
});

绑定器首先检查是否有与 article 键匹配的任何 显式绑定。如果没有找到匹配项,它将根据我们之前的隐式绑定检查是否存在以下类 App\Article(命名空间 + 首字母大写的键)。如果找到它,则它将像显式绑定一样在类上调用 firstOrFail 并将返回的实例注入到路由中。但是,如果没有找到具有此名称的类,它将继续到下一个绑定(如果有的话),并在没有找到匹配的绑定时返回未更改的路由参数。

自定义键名

类似于显式绑定,我们可以通过覆盖Eloquent模型上的getRouteKeyName方法,指定另一列来通过它检索模型实例。

/**
 * Get the route key for the model.
 *
 * @return string
 */
public function getRouteKeyName()
{
    return 'slug';
}
与仓库的隐式绑定

我们可以将隐式绑定用于除Eloquent模型之外的类,例如,如果我们使用Repository模式并且希望绑定使用仓库类而不是Eloquent模型,我们可以这样做。

仓库类的名称通常在Eloquent模型名称旁边使用一个前缀和/或一个后缀,例如,Article Eloquent模型可能有一个对应名称为EloquentArticleRepository的仓库类,我们可以将隐式绑定设置为使用此前缀和/或后缀,如下所示

$binder->implicitBind('App\Repositories', 'Eloquent', 'Repository');

(当然,如果我们不使用它,可以省略prefix和/或suffix

因此,在这个例子中

$router->get('articles/{article}', function($myArticle) {
    //
});

绑定器将检查以下类是否存在App\Repositories\EloquentArticleRepository(命名空间+前缀+首字母大写的键+后缀),如果找到,它将使用来自getRouteKeyName的列调用firstOrFail(因此您应该在您的仓库上使用这些方法)。

使用自定义方法

如果您想在您的类中使用自定义方法来检索模型实例,可以将方法名作为第四个参数传递。

$binder->implicitBind('App\Repositories', 'Eloquent', 'Repository', 'findForRoute');

这样,绑定器将调用我们的仓库上的自定义方法findForRoute,传递路由通配符值,并期望它返回解析后的实例。

使用Repository模式时使用自定义方法的隐式绑定的示例

1- 在服务提供程序中定义我们的绑定

$binder->implicitBind('App\Repositories', '', 'Repository', 'findForRoute');

2- 在routes.php中定义我们的路由

$router->get('articles/{article}', function(App\Article $article) {
    return view('articles.view', compact('article'));
});

3- 在apps/Repositories/ArticleRepository.php中添加我们的自定义方法

/**
 * Find the Article for route-model-binding
 *
 * @param  string $val  wildcard value
 *
 * @return \App\Article
 */
public function findForRoute($val)
{
    return $this->model->where('slug', $val)->firstOrFail();
}
处理 NotFound 异常

与显式绑定和隐式绑定类似,我们可以通过将闭包作为第五个参数传递给方法implicitBind来处理解析方法中抛出的异常(模型firstOrFail或在我们的仓库中)。

3) 复合绑定

有时,您将有一个包含相关模型通配符的二级或多级路由,如下所示

$router->get('posts/{post}/comments/{comment}', function(App\Post $post, App\Comment $comment) {
    //
});

在这个例子中,如果我们使用显式或隐式绑定,每个模型将被单独解析,彼此之间没有关系。有时这很好,但如果我们想在一次绑定中解析这些模型以处理它们之间的关系并可能执行适当的预加载,而无需为每个模型重复该过程,那么复合绑定就派上用场了。

复合绑定中,我们告诉绑定器为特定顺序中的多个通配符注册绑定。

我们使用方法compositeBind,将通配符名称数组作为第一个参数,将解析器回调(可以是闭包或Class@method可调用样式)作为第二个参数。

// Using a 'Class@method' callable style
$binder->compositeBind(['post', 'comment'], 'App\Repositories\PostRepository@findPostCommentForRoute');

// Using a closure
$binder->compositeBind(['post', 'comment'], function($postKey, $commentKey) {
    $post = \App\Post::findOrFail($postKey);
    $comment = $post->comments()->findOrFail($commentKey);

    return [$post, $comment];
});

注意:此绑定将匹配具有只有正好给定通配符的路由(在这种情况下为{post}{comment}),并且它们以相同的顺序出现。解析器回调将处理通配符值,并且必须返回与通配符相同的数量和顺序的解析后的模型数组。

注意:此类绑定优先于任何其他类型的绑定,这意味着在先前的示例中,如果我们为post和/或comment有显式或隐式绑定,只要整个路由匹配复合绑定,它们都不会发生。

处理 NotFound 异常

与显式和隐式绑定类似,我们可以通过将闭包作为第三个参数传递给方法compositeBind来处理解析回调中抛出的异常。

DingoAPI 集成

注意 本文档适用于 dingo/api 版本 2.*,对于较早版本的dingo,请参阅此 链接

要将 dingo/apiLumenRouteBinding 集成,您只需将默认的 dingo 服务提供者的注册替换为 LumenRouteBinding 一起提供的自定义服务提供者。

因此,请从 bootstrap/app.php 中删除以下行

$app->register(Dingo\Api\Provider\LumenServiceProvider::class);

并添加以下行代替

$app->register(mmghv\LumenRouteBinding\DingoServiceProvider::class);

(不要忘记同时注册 LumenRouteBinding 服务提供者本身)

就这样,现在您应该可以使用 LumenRoutebindingDingoAPI 一起使用了。

贡献

如果您发现了问题,请在这里报告

欢迎提交拉取请求,只需确保遵循 PSR-2 标准,并不要忘记添加测试。

许可证 & 版权

版权所有 © 2016-2023,Mohamed Gharib。在 MIT 许可证 下发布。