【问题标题】:should I inject repository class or model class in controller's constructor?我应该在控制器的构造函数中注入存储库类或模型类吗?
【发布时间】:2018-11-25 19:31:30
【问题描述】:

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

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

1) 我可以在控制器中有一个构造函数并在那里注入 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 模型并将其注入到构造函数中。

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

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

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

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

【问题讨论】:

    标签: php laravel


    【解决方案1】:

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

    如果您有一个需要换出存储层的大型应用程序,那就不好了。如果您永远不会将存储层从 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)。假设我一直都知道我只会使用 雄辩,我不想将我的代码与框架本身分离。 那么问题是为什么要使用存储库类,如图所示 第三个和第四个例子?我问这个是因为人们这么说 最好将复杂的逻辑放在存储库中,而不是放在模型中。

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

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

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

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

    这样你就可以换掉实现而不需要改变上面的代码。

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

    【讨论】:

    • 说得好,点赞。我想补充一点,如果您决定使用存储库模式,您应该将数据作为数组返回,而不是集合或 eloquent 模型。否则,它会破坏使用存储库模式的全部目的。
    • 我同意@Polaris,已更改实现以返回一个数组。谢谢!
    • 谢谢。我认为您的第二个答案是错误的,因为我的第二个问题是其他问题。我还看到人们在构造函数中注入存储库(不是接口)。因为他们不希望存储库模式(交换实现),但他们希望将函数(巨大的复杂函数)放在存储库中,而不是直接在模型中。
    • @NikaKhurashvili 在构造函数中注入特定实现违背了存储库模式的目的,并且是一种不好的做法。这样做的人只是做错了。你永远不会在任何好的框架或包中看到这一点。
    • 我并不是说在构造函数中传递存储库类是存储库模式。我知道存储库模式是什么。我刚刚看到人们将特定存储库类传递给构造函数的示例,因为他们不想在控制器或模型中编写大而庞大的逻辑。你明白吗?
    猜你喜欢
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多