倉庫模式被誤用了嗎葫笼?—— laravel

什么是倉庫模式(The Repository Pattern)麻裁?

Use a repository to separate the logic that retrieves the data and maps it to the entity model from the business logic that acts on the model. The business logic should be agnostic to the type of data that comprises the data source layer. (…) The repository mediates between the data source layer and the business layers of the application. It queries the data source for the data, maps the data from the data source to a business entity, and persists changes in the business entity to the data source. A repository separates the business logic from the interactions with the underlying data source or Web service.?—??mdsn.microsoft.com

簡單來說, 倉庫模式就是一種存放數(shù)據(jù)訪問邏輯的容器, 它向業(yè)務(wù)層屏蔽了數(shù)據(jù)訪問邏輯的細節(jié), 在不清楚數(shù)據(jù)層設(shè)計結(jié)構(gòu)的情況下, 我們也能按照業(yè)務(wù)邏輯來訪問數(shù)據(jù)層箍镜。

如圖:


Repository Pattern

可能你會疑問,檢索數(shù)據(jù)并映射到實體模型煎源,這不是 Eloquent 做的嗎?Eloquent 的功能確實如此香缺,但它不是倉庫模式手销,而是 ORM(Object-Relational Mapper),它只是讓我們以面向?qū)ο蟮姆绞皆L問數(shù)據(jù)庫更容易图张,通過使用描述對象和數(shù)據(jù)庫之間映射的元數(shù)據(jù)锋拖,將程序中的對象自動持久化到關(guān)系數(shù)據(jù)庫中。

一些倉庫模式的問題

我看過很多文章中倉庫模式是這么實現(xiàn)的:

<?php
namespace App\Repositories;

class EloquentUserRepository implements UserRepository {
    public function getAllUsers() {
        return $this->model->all();
    }
    public function getUser($id) {
        return $this->model->find($id);
    }
}

這段代碼有什么問題呢祸轮?第一個錯誤是:方法的命名兽埃。因為我們已知我們需要訪問的就是userRepository,所以方法中永遠不應(yīng)該存在user這樣的關(guān)鍵字适袜。更好的方式是:

$userRepository->all();
$userRepository->find($id);

因為這些文章的誤導(dǎo)柄错,開發(fā)者可能會認為可以添加一個類似于這樣的方法:

public function getAdults()
{
    $users = $this->model->where("age", >=, 18)->get();
    return $users;
}

這是錯誤的,因為它只實現(xiàn)了業(yè)務(wù)邏輯,與數(shù)據(jù)庫本身無關(guān)售貌,不符合倉庫模式的設(shè)計哲學(xué)给猾。

上述代碼還有一個錯誤是:在倉庫中返回 Eloquent 模型,這會使你的業(yè)務(wù)邏輯層跟 Eloquent 耦合颂跨。而且敢伸,一開始就建立倉庫是沒有意義的,它只是 Eloquent 查詢的抽象恒削,根據(jù)定義池颈,ORM 抽象不是倉庫模式。

那么钓丰,如果返回自定義的對象并且在上層邏輯中不再使用 Eloquent 呢躯砰?這種方式當然可以,但是這會讓你不能使用 Laravel 中很多重要的功能斑粱。

這個倉庫模式的例子只是一個簡單的demo弃揽,還有一些更高級的實現(xiàn)是先寫倉庫的接口,然后實現(xiàn)這個接口则北,并注冊到服務(wù)提供者矿微,這種方式確實看起來更完美了,但是也更復(fù)雜了尚揣。

在 Laravel 中文官方文檔中涌矢,推薦的最佳實踐有說,“絕不 使用 Repository快骗,因為我們不是在寫 JAVA 代碼娜庇,太多封裝就成了「過度設(shè)計(Over Designed)」,極大降低了編碼愉悅感方篮,使用 MVC 夠傻夠簡單”名秀。他們也確實遵循了, learnku開源論壇的代碼 中藕溅,沒有使用倉庫模式匕得,但是也足夠優(yōu)雅,可讀性絲毫不差巾表。

Service層

可能有人會問汁掠,“那如果不使用倉庫模式,怎么讓 controllers 更瘦呢”集币?其實仔細想想考阱,這是個偽命題。如果你是正確的使用了倉庫模式鞠苟,controllers 其實不會變得更瘦乞榨。因為 Repository 只不過是一個特定的持久化適配器秽之,它不應(yīng)該實現(xiàn)任何業(yè)務(wù)邏輯和應(yīng)用程序邏輯。

要想 controllers 變瘦姜凄,應(yīng)該使用 Service 層政溃。

摘一段 quora 上的回答:

A “Service Layer” exists between the UI and the backend systems that store data and is in charge of managing the business rules of transforming and translating data between those two layers.?—?Cliff Gilley, Quora

Service 層位于表示層和數(shù)據(jù)庫層之間,所以這是放置所有業(yè)務(wù)邏輯的地方态秧。Laravel 應(yīng)用中一般會包含以下4層:

  • UI
  • Controller
  • Service
  • Database/Eloquent

一個簡單的 service 可能長這樣:

class UserService
{
    protected $user;
    public function __construct(User $user)
    {
        $this->user = $user;
    }
    public function create(array $attributes)
    {
        $user = $this->user->newInstance();
        $user->fill($attributes)
        $user->save();
        return $user;
    }
}

在 controller 中依賴注入:

class UserController extends Controller
{
    protected $userService;

    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }
    public function store(CreateUserRequest $request)
    {
        $user = $this->userService->create(
            $request->except(['_token'])
        );
        return redirect()->route('user.show', ['id' => $user->id]);
    }
}

假設(shè)董虱,之后如果加了一個需求,用戶年齡小于18歲申鱼,將用戶的 status 字段變成0愤诱。

如果你是將這段業(yè)務(wù)邏輯放在 repository 中,那么就打破了 repository 中不能實現(xiàn)業(yè)務(wù)邏輯的規(guī)則了捐友。但如果你用的是 Service淫半,那只需要改變 UserService 中的 create 方法即可:

public function create(array $attributes)
{
    $user = $this->user->newInstance();
    if($attributes->age < 18) {
        $attributes->status = 0;
    }   
    $user->fill($attributes)
    $user->save();
    return $user;
}

當然,之前的 getAdults 方法也能放在 UserService 里面匣砖。

總結(jié)

如果是一些簡單的應(yīng)用科吭,service 層甚至也可以不需要,查詢邏輯放在 Model 中就好了猴鲫。還可以利用 Trait 來精簡邏輯代碼量对人,提高可讀性。

如果項目比較復(fù)雜拂共,那么service 層是必須的牺弄,如果你仍然要引入 repository, 比如 l5-repository宜狐,那么推薦這樣使用:

public function getAdults() {
    $users = $this->userRepo->where('age', >=, 18)->get();
    return $users;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末势告,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子抚恒,更是在濱河造成了極大的恐慌咱台,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俭驮,死亡現(xiàn)場離奇詭異吵护,居然都是意外死亡,警方通過查閱死者的電腦和手機表鳍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祥诽,“玉大人譬圣,你說我怎么就攤上這事⌒燮海” “怎么了厘熟?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我绳姨,道長登澜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任飘庄,我火速辦了婚禮脑蠕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘跪削。我一直安慰自己谴仙,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布碾盐。 她就那樣靜靜地躺著晃跺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪毫玖。 梳的紋絲不亂的頭發(fā)上掀虎,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音付枫,去河邊找鬼急凰。 笑死,一個胖子當著我的面吹牛窟勃,可吹牛的內(nèi)容都是我干的斗搞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼叶眉,長吁一口氣:“原來是場噩夢啊……” “哼址儒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起衅疙,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤莲趣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后饱溢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喧伞,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年绩郎,在試婚紗的時候發(fā)現(xiàn)自己被綠了潘鲫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡肋杖,死狀恐怖溉仑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情状植,我是刑警寧澤浊竟,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布怨喘,位于F島的核電站,受9級特大地震影響振定,放射性物質(zhì)發(fā)生泄漏必怜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一后频、第九天 我趴在偏房一處隱蔽的房頂上張望梳庆。 院中可真熱鬧,春花似錦徘郭、人聲如沸靠益。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胧后。三九已至,卻和暖如春抱环,著一層夾襖步出監(jiān)牢的瞬間壳快,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工镇草, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留眶痰,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓梯啤,卻偏偏與公主長得像竖伯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子因宇,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容