lik*_*rRr 7 php design-patterns repository-pattern 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'));
}
}
Run Code Online (Sandbox Code Playgroud)
如果我想使用该Repository模式,那么我不能使用这种方法,因为在这种情况下控制器会清楚地意识到模型的存在Article? 以这种方式使用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'));
}
}
Run Code Online (Sandbox Code Playgroud)
2)假设,我上面的代码使用Repository是正确的.现在我想在每次显示时增加文章视图计数器,但是,我想在中进行此处理Event.也就是说,代码如下:
// 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');
}
}
Run Code Online (Sandbox Code Playgroud)
在这一点上,我再次对如何实现事件处理感到困惑.我不能$article直接将模型传递给事件,因为它再次违反了OOP的原则,我的订阅者将知道文章模型的存在.所以,我不能这样做:
// controllers/ArticlesController.php
...
\Events::fire('article.shown', $article);
...
// some event subscriber
...
public function onShown(Article $article)
{
$article->increment('views');
}
...
Run Code Online (Sandbox Code Playgroud)
另一方面,我没有看到引入subscriber存储库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');
}
...
Run Code Online (Sandbox Code Playgroud)
此外,在Event处理(即增加视图计数)之后,控制器必须知道更新的模型,因为在视图中我想显示更新的视图计数器.事实证明,我仍然需要从中返回一个新模型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');
}
Run Code Online (Sandbox Code Playgroud)
我可以以某种方式将找到的模型传递回存储库并增加她的计数器(这与Repository模式的方法相矛盾吗?)?像这样的东西:
public function onShown($slug)
{
$article = $this->articleRepository->findBySlug($slug);
$this->articleRepository->updateViews($article);
}
// ArticleRepository.php
...
public function updateViews(Article $article) {
$article->increment('views');
}
...
Run Code Online (Sandbox Code Playgroud)
因此,我将尝试制定更紧凑的:
如果我使用Repository模式,我将不得不拒绝将模型直接传递给DI提供的控制器和其他舒适设备?
是否可以使用存储库来保持模型的状态并在实体之间传递(例如,从过滤器到控制器从控制器到Event后面),避免对db进行重复调用,这种方法是否正确(模型)持久性)?
这些事情,这些是我的问题.我想听听答案,想法,评论.也许,我应用模式的方法不正确?现在它引起了比解决数据映射问题更多的麻烦.
我还阅读了一些关于Repository实现的文章:
但它并没有解决我的误解
存储库模式有其优点和缺点。
从我最近采用的模式来看,它提供了更容易的测试体验 - 特别是在利用继承和多态性时。
以下是我使用的近乎包罗万象的存储库合同的摘录。
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);
}
Run Code Online (Sandbox Code Playgroud)
该合约相对不言自明,save()管理插入和更新实体(模型)。
从这里我将创建一个抽象类,它实现我想要使用的供应商的所有功能 - 例如 Eloquent 或 Doctrine。
值得注意的是,这个合同并不能涵盖所有事情,我目前正在创建一个单独的实现来处理多对多关系,但那是另一个故事了。
为了创建我的个人存储库类,我为每个存储库创建了另一个合同,该合同扩展了EntityRepositoryContract并说明了它们独有的功能。在用户的情况下 -registerUser(...)等等disableUser(...)。
然后,最终的类将扩展EloquentEntityRepository并实现存储库的相关合同。的类签名EloquentUserRepository类似于:
class EloquentUserRepository extends EloquentEntityRepository implements UserRepositoryContract
{
...
}
Run Code Online (Sandbox Code Playgroud)
在我自己的实现中,为了使类名更简洁,我利用命名空间为每个实现添加别名,如下所示:
use Repo\Eloquent\UserRepo; //For the Eloquent implementation
use Repo\Doctrine\UserRepo; //For the Doctrine implementation
Run Code Online (Sandbox Code Playgroud)
我尽量不要将所有存储库集中在一起,而是按功能对应用程序进行分组,以保持目录结构不那么混乱。
我跳过了很多细节,但我不想投入太多,因此请尝试继承和多态性,看看可以通过存储库实现更好的工作流程。
在我当前的工作流程中,我的测试有自己的抽象类,专门用于所有实体存储库实现的基本存储库合约,使测试在最初的几个障碍之后变得轻而易举。
祝你好运!
| 归档时间: |
|
| 查看次数: |
1361 次 |
| 最近记录: |