【问题标题】:constructor injection vs method injection构造函数注入 vs 方法注入
【发布时间】:2017-10-29 04:37:10
【问题描述】:

Laravel 鼓励依赖注入。由于我在我的项目中使用 laravel,我想我会尝试使用这种方法。

我正在通过type hinting my dependencies and letting it resolve them 使用 Laravel 的服务容器。我有四个控制器。它们都扩展了一个名为 GlobalController 的基类。我也有两个模型。它们都扩展了一个名为 GlobalModel 的基类。

我的第一次尝试是使用方法注入。 GlobalController 看起来像这样:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\GlobalModel;

class GlobalController extends Controller
{

    public function __construct()
    {
        $this->middleware(['authenticate', 'token']);
    }

    // functions that handle normal http requests and ajax requests

    }

从 GlobalController 扩展而来的控制器之一称为 UserController。它的一些功能是:

  • 索引 - 显示所有数据
  • 编辑 - 显示编辑表单
  • 更新 - 数据库更新

编辑和更新使用route-model-binding

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;

class UserController extends GlobalController
{

    public function index(User $user)
    {
        $users = $user->all();
        return view('pages/view_users')->with('users', $users);
    }

    public function edit(User $user)
    {
        return view('pages/edit_user')->with('user', $user);
    }

    public function update(Request $request, User $user)
    {
        $data = $request->all();
        if ($user->validate($data))
        {
            $user->update($data);
            return $this->successResponse($request, 'users', 'Successfully edited user');
        }
        return $this->failedResponse($request, $user);
    }

    // other functions

    }

虽然这工作正常,但 Request 和 User 被多次注入。如果我必须更改 Request 实现(例如),我将不得不手动更改许多函数以键入提示该特定 Request 对象。一点都不好。由于它们通常在大多数函数中被调用,因此我尝试进行构造函数注入。

这是使用构造函数注入的 GlobalController:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\GlobalModel;

class GlobalController extends Controller
{
    protected $request;
    protected $model; // use polymorphism

    public function __construct(Request $request, GlobalModel $model)
    {
        $this->request = $request;
        $this->model = $model;
        $this->middleware(['authenticate', 'token']);
    }

    // functions that handle normal http requests and ajax requests

}

这里是使用包含相同功能的构造函数注入的 UserController:

namespace App\Http\Controllers;

use Illuminate\Http\Request;;
use App\Models\User;

class UserController extends GlobalController
{

    public function __construct(Request $request, User $user) // use polymorphism
    {
        parent::__construct($request, $user);
    }

    public function index()
    {
        $users = $this->model->all();
        return view('pages/view_users')->with('users', $users);
    }

    public function edit(int $id)
    {
        $this->model = $this->model->find($id);
        return view('pages/edit_user')->with('user', $this->model);
    }

    public function update(int $id)
    {
        $this->model = $this->model->find($id);
        $data = $this->request->all();
        if ($this->model->validate($data))
        {
            $this->model->update($data);
            return $this->successResponse('users', 'Successfully edited user');
        }
        return $this->failedResponse();
    }

    // other functions

}

现在,我不能指望它,但我认为这种实现似乎不正确。它变得不那么可读了。 $model 和 $this 的使用让代码更加恶心。

我很困惑。我了解我可以从依赖注入中获得的好处,但我确信我的方法注入和构造函数注入的实现是非常错误的。我应该选择什么实现?或者我应该从这两个中选择任何一个?

【问题讨论】:

    标签: php laravel design-patterns laravel-5 dependency-injection


    【解决方案1】:

    我绝对更喜欢 Laravel 控制器的第一种方法。起初,您不需要在每种方法中都注入模型。 (为什么要在索引函数中注入用户模型?)。

    其次,您不能再使用 RouteModelBinding 的好处,必须手动检查具有给定 $id 的模型是否真的存在并采取相应的措施。此外,您不能使用可以处理验证和授权的特定 FormRequest,例如 CreateUserRequest。 (虽然这是一个可选功能)

    另请注意,在构造函数中注入的模型绝不是带有用户数据的“真实”模型。所以这只会让你访问雄辩的功能。所以你也可以在你的代码中使用 User::find($id) 。这总是会给你错误的。

    public function __construct(User $user)
    {
        dd($user->exists);
    }
    

    如果你想抽象一些东西,你可以在你的构造函数中注入存储库。

    public function __construct(UserRepository $userRepository)
    {
       $this->userRepository = $userRepository;
       // then the Repository is responsible for retrieving users
       // and you are not coupled to Eloquent. If you later want, you can Read
       // users from an XML File if you need
    }
    

    附加信息(有点题外话):虽然这很不常见,而且我从来不需要更改请求类,但您可以通过创建这样的自定义请求类来做到这一点:

    namespace App;
    
    
    use Illuminate\Http\Request;
    
    class MyRequest extends Request
    {
       // override request methods or add your new own methods
    }
    

    然后在全局 index.php 中:

        $response = $kernel->handle(
        // instead of Illuminate\Http\Request::capture()
        $request = \App\MyRequest::capture()
    );
    

    【讨论】:

    • “为什么要在索引函数中注入用户模型?)。” - index 函数将所有数据获取到视图中。我需要 $user 来执行 $user->all()。这很糟糕吗?
    • 或者只是调用 User::all() 。如果要获取所有用户,则不需要特定的用户实例。或者,如果您觉得这对您更好,请使用 UserRepository。
    • 啊,你说得对。我可以使用外墙!但是外墙不做同样的事情吗?您的意思是说外观在调用 all 时不会创建新的 User 实例?我想我只是想尽可能避免外墙。
    • 最终在每种情况下都会调用 Illuminate\Database\Eloquent::all()
    • 而且 Eloquent 模型也没有外观 ;) 是的,如果你调用 User::all() 没有用户模型被实例化。它只是调用 Illuminate\Database\Eloquent 的静态函数 all()。事实上,如果你调用 $user->all() 也会调用静态方法。您可以看到这表明这不是预期的用例。
    【解决方案2】:

    如果大多数方法都使用模型,请使用构造函数注入。 而且 $model 和 $this 的使用并没有错。但如果您仍想清理代码,请考虑存储库模式 (SRP)。您可以在那里管理长行代码。 看到这个stackoverflow answer - How can I organise classes in a Laravel 5 Project?。我希望这能帮助您解决困惑。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-22
      • 1970-01-01
      • 2017-03-15
      • 2011-01-01
      • 1970-01-01
      相关资源
      最近更新 更多