Repository 模式主要思想是创建一个数据操做代理层,把controller里的数据操做剥离出来,这样作有几个好处:php
把数据处理逻辑分离使得代码更容易维护laravel
数据处理逻辑和业务逻辑分离,能够对这两个代码分别进行测试编程
减小代码重复设计模式
下降代码出错的概率架构
让controller代码的可读性大大提升app
然而,据不少同窗反应,这一部分很难学。确实,要独立一个操做层出来,确实会增长大量代码,很是繁琐。若是你是小项目,未必须要使用这一模式。但若是是4-5年以上的复杂大型项目,这种模式的好处就比较明显了。框架
若是你是纯新手,建议你暂时不要往下看,先把laravel用得比较熟练后再回来学习。ide
学习Repository Pattern的意义不仅是为了使用它,更会让你深刻思考框架的分层思想,你开始不只关注怎么使用一个框架,还会想了解怎样设计一个框架,也许会成为你往高阶段编程的入口。post
虽说设计模式和语言及框架无关,可是脱离了语言及框架,咱们很难理解,因此咱们仍是在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(); } }
看这里,很明显,仓库指的就是一个仓库接口的实现;这里定义你的业务逻辑;
第四步——在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+接口,咱们会在后续系列文章里介绍;