如何在laravel中使用Repository Pattern(倉庫模式)

Laravel00發表於2021-02-24

好記性不如爛筆頭,學習php開發也不能懶,作筆記是一種學習的好習慣!
文章來自:mp.weixin.qq.com/s/g_nJn271pjRJHTE...
學習與交流:Laravel技術交流微信群

Repository 模式主要思想是建立在一個資料操作代理層上的,把controller裡的資料操作分離出來,這樣做的好處有以下幾點:

1 把資料處理邏輯分離使得程式碼更容易維護

2 資料處理邏輯和業務邏輯分離,可以對這兩個程式碼分別進行測試

3 減少程式碼重複

4 降低程式碼出錯的機率

5 讓controller程式碼的可讀性大大提高


如圖所示Repository的分層關係

圖片

然而,要獨立一個操作層出來,那就會增加大量程式碼,非常繁瑣。如果你是小專案,未必需要使用這一模式。但如果是4-5年以上的複雜大型專案,這種模式的好處就比較明顯了。

學習Repository Pattern的意義不只是為了使用它,更會讓你深入思考框架的分層思想,你開始不僅關注怎麼使用一個框架,還會想了解怎樣設計一個框架,也許會成為你往高階段程式設計的入口。當你感悟到什麼是一種思想的時候。。。

雖然說設計模式和語言及框架無關,但是脫離了語言及框架,我們很難理解,所以我們還是在laravel的語境下來學習。

public function index()
{
$posts = Post::whereIn('category_id',[1,2])->where('is_draft',0)->orderBy('created_at', 'desc')->take(5)->get();
return view('front.index',compact('posts'));
}

以上是典型的Eloquent資料查詢程式碼,如果你程式設計經驗豐富,你會發現這種程式碼在控制器裡到處都是,而且有很多是重複的,可讀性很差;我們的目標是把它精簡:

仔細觀察

Post::whereIn('category_id',[1,2])->where('is_draft',0)->orderBy('created_at', 'desc')->take(5)->get();

其實它由3部分組成.

第一是Post資料模型;

第二個是whereIn('category_id',[1,2])->where('is_draft',0)->orderBy('created_at', 'desc')->take(5),資料操作條件;

第三個是get()資料獲取的方法;

我們知道,Eloquent裡有個Query Scope,可以用來把第二部分,也就是查詢條件精簡。所以,在使用了Query Scope後,我們可以把精簡成:

Post::ofCategory([1,2])->isDraft()->orderBy('created_at', 'desc')->take(5)->get();

咋一看上去,好像也沒怎麼精簡啊,但實際上你已經實現程式碼解耦和複用了,比如說isDraft(), 這個程式碼可以到處用,而不用擔心耦合問題。

精簡程度和你的邏輯抽象程度有關,比如說你完全可以寫成:

Post::findPosts([1,2],0,'desc',5)->get();

在輕型專案中,強烈推薦使用Query Scope,這是一種良好的程式設計習慣。

在更復雜的專案中,Query Scope就不夠用了,因為它和資料模型還是一種強耦合,Repository Pattern就是要把第一,第二,第三部分全部解耦;

說到解耦,我們在Laravel的文件攻略中講過,第一神器就是PHP中的介面(Interface


下面來看例子

第一步 建立資料夾

app
Repositories
Interfaces
Implements

Interfaces裡面用來放介面,Implements用來放介面的實現;


第二步 建立一個介面

在上面的Interfaces目錄新建一個檔案PostInterface.php:

namespace App\Repositories\Interfaces;
Interface PostInterface{
   public function findPosts(Array $cat_id,$is_draft,$order,$take)
   {
   }
}


第三步 建立一個介面對應的實現

在上面的Implements目錄新建一個檔案PostRepository.php:

namespace App\Repositories\Implements;
use Post;
class PostRepository Implements PostInterface{
    public function findPosts(Array $cat_id,$is_draft,$order,$take){

        $query = Post::whereIn('category_id',$cat_id)
        ->where('is_draft',$is_draft)
        ->orderBy('created_at', $order)
        ->take($take)
        ->get();

        return $query;
    }
}

很明顯,倉庫指的就是一個倉庫介面的實現;這裡定義你的業務邏輯;


第四步 在ServiceProvider中繫結介面

開啟app/Providers/AppServiceProvider, 在register()加入程式碼:

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {

    }

    public function register()
    {

    $this->app->bind('App\Repositories\Interfaces\PostInterface', 'App\Repositories\Implements\PostRepository');
    }


}

我們知道,ServiceProvider是Laravel IOC容器實現動態換介面實現的地方,所以我們在這裡繫結一下,這樣我們在使用的時候,不直接使用介面實現,而是用ioc容器解析介面,它會幫你自動找到對應好的實現。這就意味著,以後需要更換實現,可以在這裡更換;


第五步 使用倉庫

回到我們的controller裡來

use App\Repositories\Interfaces\PostInterface;
class PostController extends BaseController{

    public function __construct(PostInterface $post){

        $this->postRepo = $post;
    }

    public function index(){

        $this->postRepo->findPosts([1,2],0,'desc',5);
    }


}

從上面的例子看,我們的業務邏輯變得非常精簡,完全不用管查詢;而且也現實了資料查詢部分的解耦;

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章