这个问题将分为几个部分,这引起了我的困惑.
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提供的控制器和其他舒适设备?
>是否可以使用存储库来保持模型的状态并在实体之间传递(例如,从过滤器到控制器从控制器到事件并返回),避免对db进行重复调用,这种方法是否正确(模型持久性)?
这些事情,这些是我的问题.我想听听答案,想法,评论.也许,我应用模式的方法不正确?现在它引起了比解决数据映射问题更多的麻烦.
我还阅读了一些关于Repository实现的文章:
> http://heera.it/laravel-repository-pattern#.VFaKu8lIRLe
> http://vegibit.com/laravel-repository-pattern
但它并没有解决我的误解
从我最近采用的模式开始,它可以提供更简单的测试体验 – 特别是在利用继承和多态时.
下面是我使用的近乎全面的存储库合同的摘录.
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 Repo\Eloquent\UserRepo; //For the Eloquent implementation use Repo\Doctrine\UserRepo; //For the Doctrine implementation
我尽量不将所有存储库聚集在一起,而是按功能对应用程序进行分组,以保持我的目录结构不那么混乱.
我正在跳过很多细节,但是我不想过多地使用继承和多态来试验你可以用Repositories实现更好的工作流程.
在我当前的工作流程中,我的测试有自己的抽象类专门用于基础存储库合同,所有实体存储库实现在最初的几个障碍之后使测试变得轻而易举.
祝你好运!