Php 我应该在控制器的构造函数中注入存储库类还是模型类?

Php 我应该在控制器的构造函数中注入存储库类还是模型类?,php,laravel,Php,Laravel,我希望你能读到这篇文章,并以非常明智的方式向我解释。我真的非常感激。我知道很多,但还是谢谢你 假设我有一个PostController控制器和一个post模型。以下是我如何编写代码的场景 我可以在控制器中有一个构造函数,并在那里注入Post模型。像这样: class PostController extends Controller { /** * Display a listing of the resource. * * @return \Illumin

我希望你能读到这篇文章,并以非常明智的方式向我解释。我真的非常感激。我知道很多,但还是谢谢你

假设我有一个PostController控制器和一个post模型。以下是我如何编写代码的场景

我可以在控制器中有一个构造函数,并在那里注入Post模型。像这样:

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    $post;
    public function __construct(Post $post){
        $this->post = $post;
    }

    public function show($id){
        return $this->post->find($id);
    }
2我可以直接在函数show函数中编写Post模型

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */


    public function show($id){
        return Post::find($id);
    }
3我可以拥有一个repository类,它扩展了我雄辩的Post模型,并将其注入构造函数

class PostRepository extends Post{


}

class PostController extends Controller{

    protected $post;
    public function __construct(PostRepository $post){
        $this->postRepo = $post;
    }

    public function show($id){
        return $this->postRepo->find($id);
    }
}
4我可以拥有postRepository,而无需注入和直接使用它

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */


    public function show($id){
        return PostRepository::find($id);
    }
我希望你能理解所有这些例子。让我们谈谈我的问题以及我是如何看待它的。在我开始讲话之前,我想让你们知道,我希望我的代码是可测试的,并且写得很好

问题1假设我使用第二个例子。我直接访问那里的Post模型。它是可测试的,因为laravel提供了一种模拟雄辩模型的方法。为什么这是一个糟糕的方法?我知道这很糟糕,我只是不知道为什么,因为我仍然可以嘲笑雄辩,并测试它

问题2第二个例子和第一个例子有什么区别?如果我可以测试它并模拟一个雄辩的模型(如果它直接在函数中访问),那么为什么要将它注入构造函数呢

问题3假设我不使用存储库模式。创建存储库类并不意味着使用存储库模式。存储库模式是在使用接口时使用的,例如,您可以从Elount交换到其他ORM。假设我一直都知道我只会使用雄辩的,我不想将我的代码与框架本身分离。然后问题是为什么要使用存储库类,如第三个和第四个示例所示?我这样问是因为人们说最好将复杂的逻辑放在存储库中,而不是放在模型中

问题4第三个和第四个例子有什么区别?我仍然可以测试第四个示例。为什么要在构造函数中注入PostRepository

问题1假设我使用第二个例子。我正在直接访问 在那里张贴模型。它是可测试的,因为laravel提供了一种模拟的方法 雄辩的模型。为什么这是一个糟糕的方法?我知道这很糟糕,我只是 不知道为什么,因为我仍然可以模仿雄辩,并测试它

只有当你有一个需要更换存储层的大型应用时,这才是糟糕的。如果您永远不会将存储层从DB更改为其他内容,那么这绝对不是一个坏方法,而且完全可以测试

问题2第二个和第一个有什么区别 实例如果我能测试它,并模仿一个雄辩的模型,如果它是直接 在函数中访问,为什么要将其注入构造函数

第二个和第一个示例之间没有区别。这是因为存储库实现不正确。它不应该扩展Post类。事实上,它应该实现一个带有select方法的接口,如下所示:

class PostDatabaseRepository extends PostRepositoryContract {
    public function show($id){
        return Post::find($id)->toArray();
    }

...
}
然后,在服务提供商中,将合同绑定到数据库存储库,如下所示:

$this->app->singleton(
    PostRepositoryContract::class, PostDatabaseRepository::class
);
这样,如果您想交换实现,只需如上所述更改绑定即可

问题3假设我不使用存储库模式。创建 存储库类并不意味着使用存储库模式。存储库 模式是指当使用接口时,您可以交换接口,例如从 对其他ORM有口才。假设我一直知道我只会使用 而且我不想将我的代码与框架本身分离。 然后问题是为什么要使用存储库类,如中所示 第三和第四个例子?我问这个是因为人们都这么说 最好将复杂的逻辑放在存储库中,而不是放在模型中

如果您知道您将只使用雄辩,那么不应该使用存储库模式

问题4第三个和第四个有什么区别 实例我仍然可以测试第四个示例。为什么要注射 构造函数中的PostRepository

您的示例的实现不正确。不应在构造函数中添加具体类以进行依赖项注入。相反,您应该像这样添加接口:

public function __construct(PostRepositoryContract $post){
    $this->postRepo = $post;
}
通过这种方式,您可以交换实现,而无需更改上述代码


此外,正如@Polaris在评论中提到的,您不应将数据作为集合或雄辩的模型返回。否则,它将破坏使用存储库模式的全部目的。返回一个数组,或者可能是一个单独的Post类,该类没有说服力,也不特定于某个实现。

说得好,向上投票。我只想补充一点,如果您决定使用存储库模式,那么应该将数据作为数组而不是集合或雄辩的模型返回。否则它将破坏使用存储库模式的全部目的。我同意@Polaris已将实现更改为返回数组。谢谢非常感谢。我想你的第二个
答案是错的,因为我的第二个问题是另外一个问题。我还看到人们在构造函数中注入存储库而不是接口。因为他们不想让存储库模式交换实现,但他们想让函数和复杂的函数在存储库中,而不是直接在模型中。@NikaKhurashvili在构造函数中注入特定的实现违背了存储库模式的目的,是一种不好的做法。这样做的人只是做错了。在任何好的框架或包中,您都不会看到这一点。我并不是说在构造函数中传递存储库类是一种存储库模式。我知道什么是存储库模式。我刚刚看到一些例子,人们将特定的存储库类传递给构造函数,因为他们不想在控制器或模型中编写大型逻辑。你明白吗?