在laravel 5中使用 repository 模式实现业务逻辑和数据访问的分离
1.概述
首先需要知道的是,设计模式和使用的框架和语言是无关的.这是一种设计思想. 要理解设计模式背后的原则.这样不管你使用的是什么技术,都能够在实践中,实现相应的设计模式.
按照最初提出者的介绍,Repository 是衔接数据映射层和领域层之间的一个纽带,作用相当于一个在内存中的域对象集合.客户端对象把查询的一些实体进行组合,并把它们提交给Repository .对象能够从Repository中移除或者添加,就好比这些对象在一个Collection对象上进行数据操作,同时映射层的代码会对应的从数据库中取出响应的数据.
Repositroy的核心是把业务逻辑和数据访问分离开 ,两者通过Repository进行通信,通俗点说,可以把Repository看成仓库管理员.我们要从仓库取东西(业务逻辑),只需要找管理员要就是了.不需要自己去找,这样能够达成解耦的目的,具体流程如下图所示.:
这种拆离有很久好处:
1.集中数据访问逻辑使代码易于维护.
2.业务和数据访问逻辑完全分离.
3.相当程序上可以减少重复的代码.
2、只是接口而已
要实现 Repository 模式,首先需要定义接口,这些接口就像 Laravel 中的契约一样,需要具体类去实现。现在我们假定有两个数据对象 Actor 和 Film。这两个数据对象上可以进行哪些操作呢?一般情况下,我们会做这些事情:
- 获取所有记录
- 获取分页记录
- 创建一条新的记录
- 通过主键获取指定记录
- 通过属性获取相应记录
- 更新一条记录
- 删除一条记录
现在你已经意识到如果我们为每个数据对象实现这些操作要编写多少重复代码!当然,对小型项目而言,这不是什么大问题,但如果对大型应用而言,这显然是个坏主意。
现在,如果我们要定义这些操作,需要创建一个 Repository 接口:
interface RepositoryInterface {
public function all($columns = array(''));
public function paginate($perPage = 15, $columns = array(''));
public function create(array $data);
public function update(array $data, $id);
public function delete($id);
public function find($id, $columns = array(''));
public function findBy($field, $value, $columns = array(''));
}
3、Repository 实现
下面简单实现一个封装的Repository模式的业务逻辑和数据查询的分离
我在http文件夹下创立了三个文件夹
Constracts 用于定义interface 规范一个Eloquent实例必须拥有的可用的查询方法.
Eloquent 用于定义当前模型对象的查询类.
Exception 用于定义处理查询过程中可能出现的异常的处理类.
下面是我的interface代码;
<?php
namespace App\Http\Repository\Contracts;
interface ArticleContracts
{
//select query
public function all();
public function paginate($currentPage=10);
public function find($id);
public function findOrFail($id);
//update or create query
public function create(array $data);
public function update(array $data, $id);
public function delete($id);
}
用与契约定义必须拥有的方法.
一个查询的实例必须拥有增删查改的功能.
<?php
namespace App\Http\Repository\Eloquent;
use App\Article;
use App\Http\Repository\Contracts\ArticleContracts;
class ArticleEloquent implements ArticleContracts
{
//select query
public function all($order = 'asc')
{
return Article::orderBy('id', $order)->all();
}
public function paginate($currentPage = 10)
{
return Article::orderBy('id', 'desc')->where('status', '>', 0)->paginate($currentPage);
}
public function find($id)
{
return Article::find($id);
}
public function findOrFail($id)
{
return Article::findOrFail($id);
}
public function more($data, $id)
{
return Article::with('category')->where('id', '!=', $id)->where('category_id', $data)->orderBy(\DB::raw('RAND()'))->take(6)->get();
}
//update or create query
public function create(array $data)
{
$article = new Article($data);
$article->save();
return $article;
}
public function update(array $data, $id)
{
$article = $this->findOrFail($id);
$article->update($data);
return $article;
}
public function delete($id)
{
$article = $this->findOrFail($id);
$article->delete();
}
}
这是一个简单的查询实例. 和上面所说的一样 数据处理类只负责数据的事情 与数据库交互获得了数据立刻返回给业务逻辑.
以一个简单的controller 代码来解释如何使用查询类.
public function show(ArticleEloquent $articleEloquent,$id)
{
$user = Auth::user();
$article = $articleEloquent->findOrFail($id);
//fix existing markdown content in db
if (empty($article->sourceurl)) {
$article->body = markdownto_html($article->bodyText());
} else {
$article->body = $article->bodyText();
}
$category = $article->category_id;
$user = Auth::user();
fixwzdata($article);
fixloldata($article);
//随机取出5条相同类型推荐文章
$more = $articleEloquent->more($category,$id);
return view('article.show')->withArticle($article)->withMore($more)->withUser($user);
}
这是一个全部使用了数据处理类的show函数 在函数参数中我依赖注入了数据处理类 这样这个函数就拥有了数据处理类的所有能力.
可以看到 这个地方没有一次是在和数据库交互. 全部是业务逻辑方面的处理 很好的实现了解耦.增加了代码可读性 这也是重构的意义所在.
这个人暂时没有 freestyle