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

标签 php design-patterns laravel repository-pattern

最近我开始研究Laravel 4 及其功能。我想实现存储库模式以将模型逻辑移到那里。在这一点上,我遇到了一些关于如何组织它的不便或误解。我遇到的一般问题是这样的:是否有可能在 Laravel 中实现和应用这种模式而不会让人头疼,它是否值得

这个问题会分成几个部分,这让我很困惑。

1) Laravel 提供了方便的方法将模型绑定(bind)为 Controller 参数,例如我这样做:

// 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 模式,那么我就不能使用这种方法,因为在这种情况下 Controller 会清楚地知道模型的存在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'));
    }
}

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

}

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

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

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

另一方面,我认为引入 subscriber 存储库 ArticleRepository(或将其注入(inject)订阅者的构造函数)没有任何意义,因为首先我应该找到一篇文章,然后更新计数器,最后,我将获得对数据库的额外查询(因为之前在构造函数中我也这样做):

// 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 处理后(即增加的 View 计数), Controller 有必要知道更新的模型,因为我想在 View 中显示更新的 View 计数器。事实证明,不知何故我仍然需要从 Event 返回一个新模型,但我不想 Event 成为处理特定 Action 的常用方法(为此有存储库)并返回一些值。另外,你可能注意到我的最后一个onShow()方法再次违反了Repository模式的规则,但是我不明白如何把这个逻辑放到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. 如果我要使用 Repository 模式,我将不得不拒绝将模型直接传递给 Controller ​​和 DI 提供的其他便利?

  2. 是否可以使用存储库来保存模型的状态并在实体之间传递它(例如,从过滤器到 Controller ,从 Controller 到 Event 然后返回)避免对 db 的淫秽重复调用,这种方法是否正确(模型持久性)?

这样的事情,这些是我的问题。我想听听答案、想法和评论。也许,我应用模式的方法不正确?现在它引起的问题比解决数据映射问题还多。

我还阅读了一些关于 Repository 实现的文章:

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

但这并不能解决我的误会

最佳答案

@likerRr 你问:

以这种方式使用 Repository 模式重写此示例是否正确:

首先,您应该考虑我们为什么使用设计模式,特别是存储库模式?我们使用存储库模式来实现 SOLID 原则(全部或少数)。 第一件事是不应该访问 Controller 中的数据源/数据库。这样做你是:

  1. 违反单一职责原则(S in SOLID)。您的 Controller 不得知道数据源。它只负责响应 HTTP 请求或处理您的应用程序和 HTTP。
  2. 你违反了里氏替换原则
  3. 您违反了依赖倒置原则。

因此,这就是为什么您不仅应该使用存储库模式,还应该实现 SOLID 原则。 那怎么办呢?将您的数据源访问包装在其他地方,而存储库是最好的地方。 假设您使用以下代码获取用户:

User::where('id', $id)->where('company_id', $companyId)->get();

如果您在所有需要的 Controller 中编写此代码,则不能执行以下操作:

  1. 如果不更改那一行,就不能更改数据源
  2. 你不能测试你的代码
  3. 最终这将难以维护

2:我能否以某种方式将找到的模型传回存储库并增加她的计数器(这是否与存储库模式的这种方法相矛盾?)

您在您的代码段中做得很好。 实际上,您想要同时获得 Laravel 提供的便利和模式的好处。 您可能知道您必须为另一件事牺牲一些东西。 在容易驾驶的道路上驾驶的司机不可能成为好司机。所以,我会建议你遵循设计模式和 SOLID 原则,而离开 Laravel 提供的“轻松”。否则,这所谓的“安逸”,会给你带来太多的麻烦,甚至你的项目都无法维护,一切都烟消云散。

关于使用事件的最后一件事:

事件不过是观察者模式。在你的情况下,似乎没有必要使用观察者模式,所以你应该避免它。

观察者模式/事件的最佳候选者是您正在向您的客户收费,并且在您成功收费后,您希望通过电子邮件发送当前收费金额的详细信息以及之前的大量计算。因为这需要时间,而且您不希望在完成所有这些繁重的处理时向用户显示页面重新加载。因此,您可以向用户收费、触发事件、使用成功消息将用户重定向到您的站点,并让事件处理程序完成繁重的工作,而您的用户可以做其他事情。

如果你愿意,你可以提出任何其他问题!

关于php - 使用 Laravel 实现存储库模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26744264/

相关文章:

php - 插入时出现奇怪的 Mysql 问题

java - 为什么另一个分支在我的代码中无法访问?

model-view-controller - 数据绑定(bind)是否从根本上与 MVC 不兼容?

php - 在 .blade 模板中使用 Carbon 本地化日期

laravel - 如何在 Laravel 中使用附加属性进行排序

javascript - 在vue js中使用axios post方法下载文件

php - PHP/MySQL 中的多词搜索

php - 从集合中查询关系值

php - 如何访问 mysqli_query 的数组值

Java - 处理游戏中可切换效果的最佳方法