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

时间:2018-11-25 19:31:31

标签: php laravel

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

假设我有一个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)我可以拥有一个存储库类,该类扩展了我雄辩的Post Model并将其注入到构造函数中。

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)假设我不使用存储库模式。创建存储库类并不意味着使用存储库模式。存储库模式是在使用接口时可以进行交换的(例如,从口才转换为其他ORM)。假设我一直都知道我只会用雄辩的语言,而且我不想将我的代码与框架本身分离。那么问题是,为什么要完全使用存储库类,如第三个和第四个示例所示?我之所以这样问是因为人们说最好将复杂的逻辑放入存储库而不是模型中。

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

1 个答案:

答案 0 :(得分:3)

  

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

只有当您有一个大型应用程序需要换出存储层时,这才是不好的。如果您永远都不想将存储层从数据库更改为其他任何东西,那么这根本不是一个坏方法,并且完全可以测试。

  

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

第二个例子和第一个例子之间没有区别。这是因为存储库实现不正确。它不应扩展Post类。实际上,它应该使用以下选择方法来实现接口:

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

...
}

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

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

这样,如果您想换出实现,只需如上所述更改绑定即可。

  

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

如果知道只使用Eloquent,则不应使用存储库模式。

  

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

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

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

这样,您可以换出实施而无需更改上面的代码。

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