php – 使用Laravel实现存储库模式

前端之家收集整理的这篇文章主要介绍了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提供的控制器和其他舒适设备?
>是否可以使用存储库来保持模型的状态并在实体之间传递(例如,从过滤器到控制器从控制器到事件并返回),避免对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实现更好的工作流程.

在我当前的工作流程中,我的测试有自己的抽象类专门用于基础存储库合同,所有实体存储库实现在最初的几个障碍之后使测试变得轻而易举.

祝你好运!

猜你在找的Laravel相关文章