2014-11-04 113 views
6

最近我开始研究Laravel 4及其功能。我想实现Repository模式以在那里移动模型逻辑。在这一点上,我面临着一些如何组织它的不便或误解。一般问题我有这样的事情:是否有可能实现和应用这种模式在Laravel没有头痛,并且它是否值得使用Laravel实现库模式

这个问题会被分成几个部分,这引起了我的困惑。

1)Laravel提供了将模型绑定为控制器参数的方便方式,例如,我做这种方式:

// routes.php 
Route::bind('article', function($slug) 
{ 
    return Article::where('slug', $slug)->first(); 
}); 

Route::get('articles/{article}', '[email protected]'); 

// controllers/ArticlesController.php 
class ArticlesController extends BaseController { 

    public function getArticle(Article $article) 
    { 
     return View::make('article.show', compact('article')); 
    } 
} 

如果我想使用Repository模式,那么我不能使用这种方法,因为在这种情况下,控制器将清楚地了解车型Article存在? 无论将是正确的重新写该示例中使用Repository模式这样

// routes.php 
Route::get('articles/{slug}', '[email protected]'); 

// 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是正确的。现在,我想在每次显示文章视图计数器时增加文章视图,但是,我想在Event中进行此处理。也就是说,代码如下:

// routes.php 
Route::get('articles/{slug}', '[email protected]'); 

// 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', '[email protected]'); 
    } 

} 

在这一点上我再次对如何实现事件处理感到困惑。我无法直接将$article模型传递给事件,因为它再次违反了OOP的原则,我的订阅者将知道文章模型的存在。所以,我不能这样做:

// controllers/ArticlesController.php 
... 
\Events::fire('article.shown', $article); 
... 

// some event subscriber 
... 
public function onShown(Article $article) 
{ 
    $article->increment('views'); 
} 
... 

在另一方面,我没有看到任何意义,引入subscriberArticleRepository(或用户的构造器注入的话),因为首先我应该找一个文章,然后更新计数器,最终,我会得到额外的查询(在构造函数之前,因为我这样做)到数据库:

// 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返回一个新的模型,但我不想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'); 
} 

我可以以某种方式通过发现模型回到仓库并增加她的柜台(这与Repository模式的方法相矛盾吗?)?事情是这样的:

public function onShown($slug) 
{ 
    $article = $this->articleRepository->findBySlug($slug); 
    $this->articleRepository->updateViews($article); 
} 

// ArticleRepository.php 
... 
public function updateViews(Article $article) { 
    $article->increment('views'); 
} 
... 

结果,我会尽量制定所有更紧凑:

  1. 我不得不拒绝直接传递模型,由DI提供的控制器和其他设施如果我将使用Repository模式?

  2. 是否可以使用存储库来存放模型的状态,并通过它的实体之间(例如,从过滤器控制器从控制器到Event和背面)避免淫秽重复调用数据库,是这方法将是正确的(模型持久性)?

这样的事情,这些是我的问题。我想听听答案,想法和意见。也许,我应用模式的方法不正确?现在它比解决数据映射问题更令人头疼。

此外,我读过有关仓库实现的一些文章:

  1. http://heera.it/laravel-repository-pattern#.VFaKu8lIRLe
  2. http://vegibit.com/laravel-repository-pattern

,但它并没有解决我的误解

回答

0

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); 
} 

该合同是相对言自明,save()同时管理插入和更新实体(型号)。

在这里,我将创建一个实现所有功能,因为我要使用的供应商(S)的抽象类 - 如口才或学说。

值得一提的,这个合同不会抓住每一个事情,我目前在建立一个独立的执行,处理多对多的关系,但这是另一个故事的过程。

要创建我个人仓库类,这样做,我创建一个扩展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 

我尽量不到一堆我所有的存储库一起,而不是按功能我的应用程序,以保持我的目录结构更简洁。

我跳过了对很多细节,但我不想抛出太多所以用继承和多态实验,看看有什么可以实现与库一个更好的工作流程。

从我目前的工作流程,我的测试中都有自己的抽象类专门为所有的实体仓库实施使得前几个障碍后测试轻而易举基础信息库的合同。

祝你好运!

0

它工作正常!

api.php

Route::get('/articles/{article}', '[email protected]')->middleware('can:get,article'); 

ArticleController.php

class ArticleController extends BaseController { 

    protected $repo; 

    public function __construct(ArticleRepository $repository) { 

     $this->repo = $repository; 
    } 

    public function getArticle(int $id) 
    { 
     $articleRepo = $this->repo->find($id); 
     return View::make('article.show', compact('articleRepo')); 
    } 
}