repository实作

作者: 小聪明李良才 | 来源:发表于2016-12-07 08:35 被阅读3028次

    对于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 的中大型專案架構

    相关文章

      网友评论

      • Bencho:l5-repository只是在Model层进行了分离,降低耦合。但个人觉得在业务简单或项目不大时使用,反倒增加开发繁琐度;当项目业务复杂,引入领域驱动设计,才是repository彰显重要性的时候。DDD这个模式提出有十来年啦,但可惜在php在领域驱动方面目前可借鉴的资料太少,我疑惑这是否和php本身语言特性以及分布式处理上的劣势有关
      • echoxo:laravel新人,想问楼主 再用l5-repository的的同时,如何模块化?我有找到laravel-module插件,但不知如何整合,或者说还有更好的方法?
        小聪明李良才: @echoxo 这个你要自己网上查查了
      • 小小奶狗:很强,但还是不太懂,现在正在用这个包
        小聪明李良才: @Erchoc 多看几遍就行了加油的
      • Questocat:最近在跟开发的同时争论,我认为数据的存取都应放在repository,而他们认为repository只做数据的查询,而数据的增删改应该重新放一个类,奇怪的是还命名为model。
        小聪明李良才: @emanci 嗯在业务层应该调用repository,repository内部去调用model的
        Questocat:@超级个体颛顼 但是他是这样处理的,在service中调用repository,然后service中再调用他命名的model类,进行数据的增删改 。我觉得引入repository,是为了让业务逻辑和数据访问分离,感觉他增加的这一层就显得混乱了,而且假设要换个ORM那改动岂不是大了,所以我就想探讨一下我这么思考是否合理。:smiley:
        小聪明李良才:这个个人感觉是分层问题,对于业务来说看到的是repository,通过repository来进行model的存取,至于repository里面是自己直接操作数据库,还是调用封装好的数据库操作类都是实现的问题
      • ninja911:Criteria,这个到底何意义,能否细说?
        小聪明李良才:@ninja911 这个Criteria相当于对where条件的封装,让我们能对where起一个有意义的名字,然后新增查询条件的时候,我们不需要去改model,我们只要增加新的Criteria文件即可
        Laragh:@ninja911 同求 根本不懂
      • w暗暗啊w:谢谢分享
      • 最懒进化:除了 MVC,可以有很多设计模式辅助 MVC 减肥,当然仓库模式也是不错的
        l5-repository 这个项目挺棒的 :clap:
        (文中的“对于CRUD来说,其最难的是C操作”,应该是 “R操作”吧)
        小聪明李良才: @最懒进化 嗯写错了,改过来的
      • 该叶无法找到:赞了再看:grin::+1:

      本文标题:repository实作

      本文链接:https://www.haomeiwen.com/subject/ldrdmttx.html