repository实作

Repository缘由

本文将介绍Repository的实作,基于的github项目是:l5-repository,源码是做好的教科书,代码面前所有设计意图都无所遁形。

我们首先来明确下需要解决的问题是什么,为什么会出现l5-repository这个项目。

我想你肯定遇到过这个问题:刚开始我们的Model只有几百行,但是呢,随着项目功能的不断复杂,model需要实现的功能也越来越多,当我们有一天突然回过头去看的时候,发现Model已经上千行了,我们自己也想不起来里面都有哪些功能了。

那model为什么会越来越胖呢?我们来看下model中都可能会有哪些功能。

  • php写的业务逻辑
  • 依据显示需求,我们做的数据格式转换,字段的选择性返回
  • 各种条件的查询
  • 对于创建参数、查询参数的验证
  • …..

根据分类,我们可以归纳为下面几类

  1. presenter,显示需求
  2. repository,存取需求
  3. validator,参数验证需求
  4. services,业务逻辑

于是我们就有了下面的架构图:

图片来自电灯坊
图片来自电灯坊

有了这个图以后,我们再来看l5-repository这个项目的实现,就会容易理解很多。

下面我会从3个方面来讲解l5-repository的实现:repository,presenter,validator。我们先来看repository的实现。

repository原理

开始时,我也免不了一般的套路,先来个repository的定义

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes.

其核心有两点

  • repository类似于集合,负责对象的存取,隐藏了具体的data mapping的实现
  • repository能根据client构建的查询条件,返回特定的数据

我们先来看第一点,对象存取,说到底就是CRUD操作,于是就有了repository的接口定义RepositoryInterface,具体的接口有:

对于CRUD来说,其最难的是R操作,因为会有各种各样的查询方式,提供的查询接口有:

public function find($id, $columns = ['*']);
public function findByField($field, $value, $columns = ['*']);
public function findWhere(array $where, $columns = ['*']);
public function findWhereIn($field, array $values, $columns = ['*']);
public function findWhereNotIn($field, array $values, $columns = ['*']);

然后默认的实现是Prettus\Repository\Eloquent\BaseRepository,基本上就是对Eloquent\Builder的一个封装。

但是一些更复杂的查询怎么满足呢?我们难道需要每次有新的查询都去新增findCondition接口吗?显然我们不能这么做,这个时候Criteria就隆重登场了,先看个接口:

interface CriteriaInterface
{
    /**
     * Apply criteria in query repository
     *
     * @param                     $model
     * @param RepositoryInterface $repository
     *
     * @return mixed
     */
    public function apply($model, RepositoryInterface $repository);
}

所有的Criteria都需要实现apply方法,看一个可能是实现:

class LengthOverTwoHours implements CriteriaInterface {
    public function apply($model, Repository $repository)
    {
        $query = $model->where('length', '>', 120);
        return $query;
    }
}

通过定义LengthOverTwoHours来对Model新增查询,这样子我们每次有新的查询条件,只要新建Criteria即可,满足了开放封闭原则。

接着我们来使用下l5-repository。首先通过命令php artisan make:entity Book来生成文件,然后在AppServiceProvider@register中新增

$this->app->register( RepositoryServiceProvider::class);

接着产生一个Controller

php artisan make:controller -r BookController

在里面我们可以使用注入进Repository

public function __construct( BookRepository $bookRepository )
{
    $this->bookRepository = $bookRepository;
}

然后一些具体的操作可以去看https://github.com/andersao/l5-repository,写的非常详细。

最后介绍下怎么产生criteria,通过下面的命令

php artisan make:criteria My

然后添加下面代码

class MyCriteria implements CriteriaInterface
{
    /**
     * Apply criteria in query repository
     *
     * @param Builder                    $model
     * @param RepositoryInterface $repository
     *
     * @return mixed
     */
    public function apply($model, RepositoryInterface $repository)
    {
        $model = $model->where('user_id','=', \Auth::user()->id );
        return $model;
    }
}

就能够使用了,然后在controller中,我们通过下面的方式查询

public function index()
{
  $this->repository->pushCriteria(new MyCriteria());
  $books = $this->repository->all();
  ...
}

Presenters优化

接着我们讲Presenters部分。

我们再强调下Presenter解决的问题:把日期、金额、名称之类的呈现(presentation)逻辑抽离出来。在l5-repository这个功能其实不是很满意,我们希望的是1 Presenter中介绍的那种样子,原先样子是:

class Article extends Eloquent
{
    public function getDate(){/*...*/}

    public function getTaiwaneseDateTime(){/*...*/}

    public function getWesternDateTime(){/*...*/}

    public function getTaiwaneseDate(){/*...*/}

    public function getWesternDate(){/*...*/}
}

抽离出来后是:

class Article extends Eloquent
{
    public function present()
    {
        return new ArticlePresenter($this);
    }
}

class ArticlePresenter extends Presenter {

    public function getTaiwaneseDateTime(){/*...*/}

    public function getWesternDateTime(){/*...*/}

    public function getTaiwaneseDate(){/*...*/}

    public function getWesternDate(){/*...*/}
}

下面是一些实现方案

https://github.com/laracasts/Presenter

https://github.com/robclancy/presenter

https://github.com/laravel-auto-presenter/laravel-auto-presenter

参数验证

最后我们介绍validator

validator的逻辑从model中抽离出来,单独放入一个类中,

use \Prettus\Validator\Contracts\ValidatorInterface;
use \Prettus\Validator\LaravelValidator;

class PostValidator extends LaravelValidator {

    protected $rules = [
        ValidatorInterface::RULE_CREATE => [
            'title' => 'required',
            'text'  => 'min:3',
            'author'=> 'required'
        ],
        ValidatorInterface::RULE_UPDATE => [
            'title' => 'required'
        ]
   ];

}

能够制定create和update操作的时候不同的验证规则。

总结

以上就是repository的全部,文章开头由实际项目中model越来越胖引出如何给model瘦身,接着对model中的功能进行了划分,给出了合理的项目组织方式,接着通过从repository,presenter,validator分析了具体的一些优化方式。

最后本文只是简单的对l5-repository进行了介绍,更详细的功能,更多的实现细节,你都可以clone项目下来,自己好好去看,相信会学到很多。

参考

胖胖Model減重的五個方法

Using Repository Pattern in Laravel 5

胖胖 MODEL 的減重方法:PRESENTER

Laravel 的中大型專案架構

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,331评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,372评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,755评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,528评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,526评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,166评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,768评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,664评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,205评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,290评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,435评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,126评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,804评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,276评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,393评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,818评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,442评论 2 359

推荐阅读更多精彩内容