DI

时间:2016-03-18 20:33:55

标签: php laravel dependency-injection repository-pattern

到目前为止,我认为存储库是“食谱书”,我将几乎每个查询都放在我的模型中。

例如:

Model -> Post
Repo -> PostRepository with Post injected via DI
Method -> find() uses $this->post->find

到目前为止,这对于减少重复并帮助进行单元测试非常有用。

但后来遇到了我需要另一个存储库中的存储库的情况。 然后我需要另一个存储库中的第一个存储库。 我在彼此的构造函数中注入了这两个。

我的测试会失败并会引发限制耗尽错误。无限注射是问题。

经过一番思考后,我想出了3个可能的解决方案:

1)仅在方法中注入Repos。这将解决我的实际问题,但我必须特别小心,我从不注入相同的回购并回到案例1.我讨厌这个选项。

2)存储库应该是概念,而不是模型。如果我需要另一个存储库,也许它们共享相同的概念,并且应该提取到另一个存储库。 这不会永远解决问题,因为我觉得如果应用程序增长,我将不得不在另一个存储库中注入其中一个存储库。 我对这个选项有点“好”。

3)我需要另一种类型/级别的课程。只能在最低级别查询模型的人。这些将以任何方式由任何存储库使用,并且永远不会导致任何问题。这也有助于避免因问题导致的错误1)。我最喜欢这个选项。

我觉得最好的解决方案可能是上面所有内容的混合,但我现在有点迷失了。 这显然都在Laravel环境中,尽管我觉得这比框架甚至语言层次更抽象。

感谢。

编辑:

正如在评论中提到的那样,这是一个代码的简单示例,我需要在另一个回购中查询:

class PostRepository {
    private $post;
    private $commentRepository;

    public function __construct(Post $post, CommentRepository $commentRepository) {
        $this->post = $post;
        $this->commentRepository = $commentRepository;
    }

    public function hasComments($post) {
        return $this->commentRepository->countCommentsForPost($post->id) > 0;
    }

    public function exists($post) {
        // ...
    }
}

class CommentRepository {
    private $comment;
    private $postRepository;

    public function __construct(Comment $comment, PostRepository $postRepository) {
        $this->comment = $comment;
        $this->postRepository = $postRepository;
    }

    public function create($content, $post) {
        if ($this->postRepository->exists($post)) {
            return $this->comment->create(['content' => $content, 'post_id' => $post->id]);
        }
    }

    public function countCommentsForPost($post) {
        // ...
    }
}

这是一个非常简单(而不是太聪明)的例子,你可以说Post模型应该有一个“评论”关系,但我仍然觉得这种逻辑在这里是错误的,我不知道比如把它放在控制器里。该代码只会通过“自动”DI获得无限循环。

2 个答案:

答案 0 :(得分:1)

据我所知,存储库通常用于其他所有存储库。或者,我从未真正想过你需要这样一个例子的例子。

您所描述的内容可能是一个服务类,可能会通过构造函数注入一些存储库。

哦,我读到的关于存储库的最后一件事给了我一个有趣的想法,即只读存储库只是查询数据库的结果,但他们不保存/创建/更新/删除东西。由于复杂性,这些方法留在模型中。

以下是我读过这篇文章的帖子: http://adamwathan.me/2015/02/14/active-repository-is-an-antipattern/

哦,通过单元测试存储库,你的意思是集成测试吗?因为为已经测试的东西编写单元测试可能不是最好的主意。 (谈论Eloquent)

更新: 我刚看到你的更新,似乎你需要Comment模型,而不是整个存储库。并且存储库应该尽可能短,通常一个返回语句没有任何ifs,但我从来没有遇到过这样的情况,所以我不确定检查那些东西是否是最好的主意。

也许在其他地方检查if,如果它通过则运行查询?

此外,对于某些查询,使用关系可能会对您有所帮助。

答案 1 :(得分:1)

简单的答案是,尽管您可以将DI存储库类转换为其他存储库类,但您不能像在此编码那样使用循环DI。为您解析DI的laravel容器类将调用获取DI的每个类的构造函数。因此,您需要做的是整理您的依赖关系层次结构,例如将一些常用功能移到一个依赖关系层。