dyanakiev / lurobind-2020
Lumen的路由模型绑定,由dyankiev修改
Requires
- php: >=7.1
- laravel/lumen-framework: 5 - 8
- nikic/fast-route: >=0.4 <2.0
README
使用6.0.0版本配合Lumen 8
此包为Lumen (5 - 7) 增加了Route Model Binding
支持。
众所周知,Lumen默认不支持
Route Model Binding
,因为Lumen不使用Laravel所用的Illuminate路由器,而是使用FastRoute,后者速度更快。通过此包,我们增加了强大的Route Model Binding
支持,同时在Lumen中仍然受益于FastRoute的速度。
目录
安装
使用composer
composer require mmghv/lumen-route-binding "^1.0"
它需要
php >= 7.1 Lumen 5 - 7
注册服务提供者
在下一节...
用法
路由模型绑定提供了一种方便的方法,可以将模型实例自动注入到您的路由中。例如,您可以直接注入与给定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
模型实例,因此我们可以这样定义我们的路由
$app->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');
所以在这个例子中
$app->get('articles/{article}', function($myArticle) { // });
绑定器首先会检查是否存在匹配 article
键的任何 显式绑定。如果没有找到匹配项,它将根据我们之前的隐式绑定进行检查,看是否存在以下类 App\Article
(命名空间 + key的首字母大写),如果找到,它将像显式绑定一样在类上调用 firstOrFail
并将返回的实例注入到路由中,但 然而,如果没有找到具有此名称的类,它将继续进行下一个绑定(如果有的话),并在没有绑定匹配的情况下返回未更改的路由参数。
自定义键名
类似于显式绑定,我们可以通过覆盖 Eloquent 模型上的 getRouteKeyName
方法来指定另一个列以检索模型实例。
/** * Get the route key for the model. * * @return string */ public function getRouteKeyName() { return 'slug'; }
与仓储的隐式绑定
我们可以使用除 Eloquent 模型以外的类进行隐式绑定,例如,如果我们使用类似 Repository Pattern
的东西,并且希望我们的绑定使用仓储类而不是 Eloquent 模型,我们可以这样做。
仓储类名称通常在 Eloquent 模型名称旁边使用 Prefix
和/或 Suffix
,例如,Article
Eloquent 模型可能有一个相应的仓储类,名称为 EloquentArticleRepository
,我们可以设置我们的隐式绑定使用此前缀和/或后缀,如下所示
$binder->implicitBind('App\Repositories', 'Eloquent', 'Repository');
(当然,如果我们不使用它,我们可以省略 prefix
和/或 suffix
)
所以在这个例子中
$app->get('articles/{article}', function($myArticle) { // });
绑定器将检查以下类是否存在 App\Repositories\EloquentArticleRepository
(命名空间 + 前缀 + key的首字母大写 + 后缀),如果找到,它将使用从 getRouteKeyName
获取的列调用 firstOrFail()
(因此你的仓储应该有这些方法)。
使用自定义方法
如果您想使用类上的自定义方法来检索模型实例,您可以将方法名称作为第四个参数传递。
$binder->implicitBind('App\Repositories', 'Eloquent', 'Repository', 'findForRoute');
这样,绑定器将调用我们的仓储上的自定义方法 findForRoute
,传递路由通配符值,并期望它返回解析的实例。
使用仓储模式进行隐式绑定时使用自定义方法的示例
1- 在服务提供者中定义我们的绑定
$binder->implicitBind('App\Repositories', '', 'Repository', 'findForRoute');
2- 在 routes.php
中定义我们的路由
$app->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) 复合绑定
有时,您将有一个包含相关模型通配符的两个或更多级别的路由,类似于
$app->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/api
与LumenRouteBinding
,您只需将默认的dingo
服务提供者注册替换为LumenRouteBinding
提供的自定义服务提供者。
因此,请从bootstrap/app.php
中移除此行
$app->register(Dingo\Api\Provider\LumenServiceProvider::class);
并添加此行代替
$app->register(mmghv\LumenRouteBinding\DingoServiceProvider::class);
(不要忘记同时注册LumenRouteBinding
服务提供者本身)
就是这样,现在您应该能够使用LumenRoutebinding
与DingoAPI
。
贡献
如果您发现了问题,请在此处报告。
欢迎提交拉取请求,请确保遵循PSR-2标准,并不要忘记添加测试。
许可 & 版权
版权所有 © 2016-2017,Mohamed Gharib。在MIT许可下发布。