php - 我应该在 Controller 的构造函数中注入(inject)存储库类还是模型类?

标签 php laravel

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

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

1) 我可以在 Controller 中有一个构造函数并在那里注入(inject) 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)我可以直接在一个函数中写Post模型(显示函数)。

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


    public function show($id){
        return Post::find($id);
    }

3) 我可以有一个存储库类来扩展我 Eloquent Post 模型并将其注入(inject)构造函数。

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 而无需注入(inject)并直接使用它。

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 提供了一种模拟 Eloquent 模型的方法。为什么这种方法不好?我知道这很糟糕,我只是不知道为什么,因为我仍然可以 mock eloquent 并测试它。

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

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

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

最佳答案

Question 1) let's say I use the second example. I'm directly accessing Post model there. It's testable because laravel provides a way to mock eloquent models. Why is this bad approach? I know it's bad, i just don't know why, because I can still mock eloquent and test it.

只有在您的大型应用需要换出存储层时才会出现这种情况。如果您永远不会将存储层从 DB 更改为其他东西,那么这根本不是一个坏方法,而且它是完全可测试的。

Question 2) What's the difference between the second and the first example? If I can test it and mock an eloquent model if it's directly accessed in a function, why to inject it in a constructor at all?

第二个和第一个例子没有区别。那是因为存储库实现不正确。它不应该扩展 Post 类。它实际上应该实现一个带有选择方法的接口(interface),如下所示:

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

...
}

然后,在您的服务提供商中将合约绑定(bind)到数据库存储库,如下所示:

$this->app->singleton(
    PostRepositoryContract::class, PostDatabaseRepository::class
);

通过这种方式,如果您想换出实现,只需像上面那样更改绑定(bind)即可。

Question 3) Let's say I don't use repository patterns. Creating repository classes doesn't mean using repository pattern. Repository pattern is when used interfaces and you can swap (for example from eloquent to other ORM). Let's say I always know that I will use only eloquent and I don't want to decouple my code from framework itself. Then the question is why to use Repository Classes at all as shown in the third and fourth example? I'm asking this because people say that it's better to put complex logic in repositories and not in a model.

如果你知道你只会使用 Eloquent,你就不应该使用 Repository 模式。

Question 4) What's the difference between the third and fourth example? I can still test the fourth example. why to inject PostRepository in a constructor at all?

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

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

通过这种方式,您无需更改上述代码即可换出实现。

此外,正如@Polaris 在评论中提到的,您不应将数据作为集合或 Eloquent 模型返回。否则它会破坏使用存储库模式的全部目的。返回一个数组或者可能是一个单独的 Post 类(非 Eloquent 且不特定于实现)。

关于php - 我应该在 Controller 的构造函数中注入(inject)存储库类还是模型类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53471100/

相关文章:

javascript - 在url中的ajax请求之后捕获并更新变量值

php - 如何在 Laravel 5.2 中使用不同的数据库表列名登录?

php - 一个查询中包含多个查询?

php - 如何使用 Google Cloud IAM 角色使用 Google OAuth 进行基于角色的 Web 应用程序控制

php - Laravel 迁移删除表

php - 在 Laravel (Maatwebsite\Excel\Facades\Excel) 中上传 Excel 显示未定义的数组键 "Sheet 1"

php - 如何使用javascript异步读取文件进行文件上传

php - 保存邮箱密码? - 这安全吗?

PHP SQL Server : 500 Internal server error with specific query

php - Laravel Carbon 如何在不改变小时的情况下改变时区