php – 使用Laravel实现存储库模式
最近我开始研究Laravel 4及其功能.我想实现Repository模式以在那里移动模型逻辑.在这一点上,我面临着如何组织它的一些不便或误解.一般问题我有这样的事情:是否有可能在Laravel中实现和应用这种模式而不会头痛,是否值得?
这个问题将分为几个部分,这引起了我的困惑. 1)Laravel提供了将模型绑定为控制器参数的便捷方式,例如,我是这样做的: // routes.php Route::bind('article',function($slug) { return Article::where('slug',$slug)->first(); }); Route::get('articles/{article}','ArticlesController@getArticle'); // controllers/ArticlesController.php class ArticlesController extends BaseController { public function getArticle(Article $article) { return View::make('article.show',compact('article')); } } 如果我想使用Repository模式,那么我就不能使用这种方法,因为在这种情况下控制器会清楚地意识到模型的存在吗?以这种方式使用Repository模式重写此示例是否正确: // routes.php Route::get('articles/{slug}','ArticlesController@getArticle'); // controllers/ArticlesController.php class ArticlesController extends BaseController { private $article; public function __construct(ArticleRepository $article) { $this->article = $article; } public function getArticle($slug) { $article = $this->article->findBySlug($slug); return View::make('article.show',compact('article')); } } 2)假设,我上面的代码使用Repository是正确的.现在我想在每次显示时增加文章视图计数器,但是,我想在事件中进行此处理.也就是说,代码如下: // routes.php Route::get('articles/{slug}','ArticlesController@getArticle'); // controllers/ArticlesController.php class ArticlesController extends BaseController { private $article; public function __construct(ArticleRepository $article) { $this->article = $article; } public function getArticle($slug) { $article = $this->article->findBySlug($slug); Events::fire('article.shown'); return View::make('articles.single',compact('article')); } } // some event subscriber class ArticleSubscriber { public function onShown() { // why implementation is missed described bellow } public function subscribe($events) { $events->listen('article.shown','ArticleSubscriber@onShown'); } } 在这一点上,我再次对如何实现事件处理感到困惑.我无法将$article模型直接传递给事件,因为它再次违反了OOP的原则,我的订阅者将知道文章模型的存在.所以,我不能这样做: // controllers/ArticlesController.php ... Events::fire('article.shown',$article); ... // some event subscriber ... public function onShown(Article $article) { $article->increment('views'); } ... 另一方面,我认为没有任何意义可以引入订阅者存储库ArticleRepository(或将其注入订阅者的构造函数中),因为首先我应该找一篇文章,然后更新计数器,最后,我会得到额外的查询(因为以前在构造函数中我做同样的事情)到数据库: // controllers/ArticlesController.php ... Events::fire('article.shown',$slug); ... // some event subscriber ... private $article; public function __construct(ArticleRepository $articleRepository) { $this->article = $articleRepository; } public function onShown($slug) { $article = $this->articleRepository->findBySlug($slug); $article->increment('views'); } ... 此外,在处理事件(即增加视图计数)之后,控制器必须知道更新的模型,因为在视图中我想显示更新的视图计数器.事实证明,我仍然需要从Event返回一个新模型,但我不希望Event已成为处理特定操作的常用方法(为此存在存储库)并返回一些值.另外,你可能会注意到我的最后一个onShow()方法再次违反了Repository模式的规则,但我不明白如何将这个逻辑放到存储库中: public function onShown($slug) { $article = $this->articleRepository->findBySlug($slug); // INCORRECT! because the Event shouldn't know that the model is able to implement Eloquent // $article->increment('views'); } 我可以以某种方式将找到的模型传递回存储库并增加她的计数器(它是否与存储库模式的这种方法相矛盾?)?像这样的东西: public function onShown($slug) { $article = $this->articleRepository->findBySlug($slug); $this->articleRepository->updateViews($article); } // ArticleRepository.php ... public function updateViews(Article $article) { $article->increment('views'); } ... 因此,我将尝试制定更紧凑的: >如果我要使用Repository模式,我将不得不拒绝将模型直接传递给DI提供的控制器和其他舒适设备? 这些事情,这些是我的问题.我想听听答案,想法,评论.也许,我应用模式的方法不正确?现在它引起了比解决数据映射问题更多的麻烦. 我还阅读了一些关于Repository实现的文章: > http://heera.it/laravel-repository-pattern#.VFaKu8lIRLe 但它并没有解决我的误解
存储库模式有它的优点和缺点.
从我最近采用的模式开始,它可以提供更简单的测试体验 – 特别是在利用继承和多态时. 下面是我使用的近乎全面的存储库合同的摘录. interface EntityRepository { /** * @param $id * @return array */ public function getById($id); /** * @return array */ public function getAll(); /** * @param array $attr * @return array */ public function save(array $attr); /** * @param $id */ public function delete($id); /** * Checks if a record with the given values exists * @param array $attr * @return bool */ public function exists(array $attr); /** * Checks if any records with any of these values exists and returns true or false * @param array $attr * @return bool */ public function unique(array $attr); } 合同是相对自我解释的,save()管理插入和更新实体(模型). 从这里开始,我将创建一个抽象类,实现我想要使用的供应商的所有功能 – 例如Eloquent或Doctrine. 值得注意的是,这个合同不会捕捉到所有的东西,我目前正在创建一个处理多对多关系的单独实现,但这是另一个故事. 为了创建我的个人存储库类,为此,我为扩展EntityRepositoryContract的每个存储库创建了另一个合同,并说明了它们独有的功能.在用户的情况下 – registerUser(…)和disableUser(…)等. 然后,最终的类将扩展EloquentEntityRepository并实现存储库的相关合同. EloquentUserRepository的类签名可能是这样的: class EloquentUserRepository extends EloquentEntityRepository implements UserRepositoryContract { ... } 在我自己的实现中,为了使类名更简洁,我利用命名空间来为每个实现添加别名,如下所示: use RepoEloquentUserRepo; //For the Eloquent implementation use RepoDoctrineUserRepo; //For the Doctrine implementation 我尽量不将所有存储库聚集在一起,而是按功能对应用程序进行分组,以保持我的目录结构不那么混乱. 我正在跳过很多细节,但是我不想过多地使用继承和多态来试验你可以用Repositories实现更好的工作流程. 在我当前的工作流程中,我的测试有自己的抽象类专门用于基础存储库合同,所有实体存储库实现在最初的几个障碍之后使测试变得轻而易举. 祝你好运! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |