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



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

  1. 我可以在控制器中有一个构造函数,并在那里注入Post模型。像这样:
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* @return IlluminateHttpResponse
*/
$post;
public function __construct(Post $post){
$this->post = $post;
}
public function show($id){
return $this->post->find($id);
}
  1. 我可以直接在函数中编写Post模型(显示函数(
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* @return IlluminateHttpResponse
*/

public function show($id){
return Post::find($id);
}
  1. 我可以有一个存储库类来扩展我的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);
}
}
  1. 我可以拥有postRepository,而无需注入和直接使用它
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* @return IlluminateHttpResponse
*/

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

让我们谈谈我的问题以及我如何看待它。在我开始讨论之前,我想让你知道,我希望我的代码是可测试的,并且写得很好。

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

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

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

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

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

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

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

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

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

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

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

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

问题3(假设我不使用存储库模式。正在创建存储库类并不意味着使用存储库模式。存储库模式是当使用接口时,您可以交换(例如对其他ORM有说服力(。比方说,我一直知道我只会使用雄辩,我不想把我的代码和框架本身脱钩。那么问题是为什么要使用Repository类,如中所示第三个和第四个例子?我这么问是因为人们这么说最好将复杂的逻辑放在存储库中,而不是放在模型中。

如果您知道只使用Eloquent,则不应使用Repository模式。

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

您的示例的实现不正确。您不应该在构造函数中添加用于依赖项注入的具体类。相反,您应该添加这样的界面:

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

通过这种方式,您可以交换实现,而无需更改上面的代码。

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

最新更新