Laravel與Repository Pattern(倉庫模式)

爲何要學習Repository Pattern(倉庫模式)

Repository 模式主要思想是創建一個數據操做代理層,把controller裏的數據操做剝離出來,這樣作有幾個好處:php

  • 把數據處理邏輯分離使得代碼更容易維護
  • 數據處理邏輯和業務邏輯分離,能夠對這兩個代碼分別進行測試
  • 減小代碼重複
  • 下降代碼出錯的概率
  • 讓controller代碼的可讀性大大提升

然而,據不少同窗反應,這一部分很難學。確實,要獨立一個操做層出來,確實會增長大量代碼,很是繁瑣。若是你是小項目,未必須要使用這一模式。但若是是4-5年以上的複雜大型項目,這種模式的好處就比較明顯了。html

若是你是純新手,建議你暫時不要往下看,先把laravel用得比較熟練後再回來學習。laravel

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

什麼是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數據查詢代碼,若是你編程經驗豐富,你會發現這種代碼在控制器裏處處都是,並且有不少是重複的,可讀性不好;咱們的目標是把它精簡:架構

仔細觀察app

  1. 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後,咱們能夠把精簡成:ide

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

咋一看上去,好像也沒怎麼精簡啊,但實際上你已經實現代碼解耦和複用了,好比說isDraft(), 這個代碼能夠處處用,而不用擔憂耦合問題。post

精簡程度和你的邏輯抽象程度有關,好比說你徹底能夠寫成:

  1. 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);
}
}

這樣你看,第一,咱們的業務邏輯變得很是精簡,徹底不用管查詢;第二,現實了數據查詢部分的解耦;

到這裏,有同窗就會問了,一開始說好的三個部分解耦呢,你這裏只實現了第二部分啊;

確實,爲了最快讓你們明白什麼是Repository,我把第一和第二部分的解耦省略了,咱們放到這篇文章的系列後續講。

你或許還有很多疑惑,我費那麼大勁,寫成最後這個樣子,好像也沒什麼區別啊。聰明的同窗可能想到一點,若是採用Repository Pattern的話,是否是意味着之後我能夠先在controller裏寫成$this->postRepo->findPosts([1,2],0,'desc',5); 具體的查詢邏輯先不寫,而後我快速先把 整個應用的業務邏輯先跑一遍,而後再回頭一個一個寫接口實現來支持業務邏輯;(哇擦,太NB了,媽媽不再用擔憂SB客戶/PM改變需求了);

恭喜,你已經進入高級編程裏說的DDD(Domain Driven Design 領域驅動設計)大門了,事實上,整個Laravel框架的核心架構就是這樣乾的,IOC+接口,咱們會在後續系列文章裏介紹;

轉載:http://www.cnblogs.com/zhangwei595806165/p/5735959.html

相關文章
相關標籤/搜索