優雅的使用路由模型繫結

Max發表於2018-09-21

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 協議》,轉載必須註明作者和本文連結

相關文章