优雅的使用路由模型绑定

Basic#

laravel5.1/5.2 发布的路由模型绑定是一个非常强大的功能,dingo/api 中想要使用路由模型绑定需要引入 bindings 组件到 dingo 的路由组中

路由中的参数命名#

如有需要,使用资源的单数形式为路由命名,而不是 {id}/{slug}/{code} 等等

正确的示例

# api.php

Route::resource('posts', 'PostController'); // laravel会将参数命名为 post
Route::get('users/{user}/posts', 'PostController@index');
Route::get('posts/{post}/comments', 'CommentController@index');

显式绑定#

简书的文章为例,使用 RESTFul 风格可以得到以下几条路由

# api.php

Route::get('posts', 'PostController@index'); // 首页/基础帖子展示
Route::get('users/{user}/posts', 'PostController@index'); // 某个用户的帖子
Route::get('collections/{collection}/posts', 'PostController@index') // 某个专题下的帖子

可以看到,这里我使用了同一个方法来处理这三条路由.

接下来定义路由模型绑定,这里使用显式绑定,以获得较大的灵活性

# RouteServiceProvider.php

Route::model('user', User::class);
Route::model('collection', Collection::class)

然后看看控制器中的定义

# PostController.php

public function index($parent = null)
{
    $query = $parent ? $parent->posts() : Post::query();

    // e.g
    $posts = $query->latest()->paginate();

    // ...
}

当我们使用第一条路由访问我们的帖子时,$parent 得到的是一个 null, $query = Post::query.

访问后两条路由时,由于路由模型绑定,$parent 被赋值为具体的 model. 此时可以通过 model 中定义的关联关系来获取 query. 通过显示绑定和关联关系的定义,使得 $parent->posts () 足够抽象,不依赖于具体的 model. 具有强大的通用性.

p.s#

# User.php

public function posts()
{
    return $this->hasMany(Post::class);
}
# Collection.php

public function posts()
{
    return $this->belongsToMany(Post::class, 'collection_post');
}

Advance#

对于,如 我的文章列表,我的订单等我们可能会这样定义我们的 RESTFul 路由

// 这里单数形式的user就代表着me的意思, 参考于github api
Route::get('user/posts', 'PostController@index');
Route::get('user/orders', 'OrderController@index');

甚至,依旧已简书为例,简书有两个板块 30 日热门7 日热门 , 我们可能会有这样两条路由

Route::get('hot-30/posts', 'PostController@index');
Route::get('hot-7/posts', 'PostController@index');

// 又或者根据推荐算法通过用户画像推荐不同的文章
Route::get('recommend/posts', 'PostController@index');

不要激动,接下来不是算法环节?

? 现在的问题是,如何依旧使用同一个方法实现?的几条路由呢?

我们换一种思路。前面我们都是在控制器层面做抽象,然后把具体逻辑交给路由层。可面对上面的需求依旧有些力不从心,那我们不妨寻找一下上面需求的共同点,再提取一层抽象

当然更简单的思路是 拆开几个方法写就 ok 啦,搞这么麻烦是吧。见仁见智。瞎折腾就是了

我们可以这么做

# api.php

// 使用 {virtual} 来匹配上面的hot-30,hot-7,recommend 等等
Route::get('{virtual}/posts', 'PostController@index'); 
# RouteServiceProvider.php

Route::bind('virtual', function ($value) {
    $virtual = "App\\Virtual\\" . studly_case($value);
    return new $virtual($value);
});

上面的做法是,路由模型绑定是基于 model, 或者说 entity 的 (在 symfony 中 model 被称作 entity). 但是 hot-30/hot-7/recommend 并不基于 model.(当然也可以基于 model, 不过这不是我们本次讨论的重点),

那我们不妨使用一个 virtual 来承载它们,virtual 是一个和 entity 相近又相反的意思。在这里再适合不过了。来看看具体实现

# Hot30.php

namespace App\Virtual;

use App\Models\Post;

class Hot30 extends Virtual
{
    public function posts()
    {
        $ids = ...; // service
        return Post::whereIn('id', $ids);
    }
}

Hot7,Recommend 同理。这样我们又承接起了上面控制器的代码。这里的 posts () 的作用就相当于比如 User.php 中的 posts () 的作用,但是却更加的灵活.

鲁迅说过:不要害怕在你的 app 下添加目录

我只是分享了一个简单的想法,更多的用法等着你来探索.

successful!

本作品采用《CC 协议》,转载必须注明作者和本文链接
我正在全力开发 nature 编程语言,如果我的文章对你有帮助,希望能获得一个 star,这对我的帮助非常大。
本帖由系统于 3年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 6

一直都在用 dingo/api, 怪不得每次
Route::get('posts/{post}', 'PostController@show');
在 show 方法中模型绑定取到的值都是空的

6年前 评论
liyu001989

laravel5.5 发布的路由模型绑定是一个非常强大的功能,然而 dingo/api 由于自定义了路由模块迟迟不支持该功能,直到摆脱了 dingo/api 后,才能体验一把路由模型绑定.

错误:

  1. 5.1 已经有了路由模型绑定,5.2 增加了隐式绑定;
  2. 路由模型绑定通过 bindings 中间件完成,dingo/api 中增加该中间件即可
6年前 评论

@liyu001989 哈哈,知道了。我之前看到这个 https://github.com/dingo/api/issues/1506 当时还没有解决

6年前 评论

鲁迅:我没说过 [doge]

6年前 评论

Route::get ('collections/{collections}/posts', 'PostController@index') // 某个专题下的帖子

{collections}?{collection}

6年前 评论

@bottleleong 笔误,应该是 {collection}

6年前 评论