【问题标题】:Laravel Controller Dependency InjectionLaravel 控制器依赖注入
【发布时间】:2020-01-31 01:49:29
【问题描述】:

我正在尝试在 Laravel 中进行依赖注入,以使我的控制器和模型尽可能精简。目标是拥有存储库来处理归因于某些模型的数据的获取。

为此,我正在尝试遵循文档 here 和流行的 Laravel 样板 here

中的示例

但我不明白$user 的来源。

所以看看样板文件,我们有两个文件:

ProfileController here

摘录如下:

use App\Repositories\Frontend\Access\User\UserRepository;
/**
 * Class ProfileController.
 */
class ProfileController extends Controller
{
    /**
     * @var UserRepository
     */
    protected $user;
    /**
     * ProfileController constructor.
     *
     * @param UserRepository $user
     */
    public function __construct(UserRepository $user)
    {
        $this->user = $user;
    }

这看起来很像文档中提到的依赖注入,是这样的:

class UserController extends Controller {

     /**
         * The user repository instance.
         */
        protected $users;

        /**
         * Create a new controller instance.
         *
         * @param  UserRepository  $users
         * @return void
         */
        public function __construct(UserRepository $users)
        {
            $this->users = $users;
        }

我的问题是我不明白$user 的来源。

UserRepository 中没有 $user 定义为类本身的参数。代码中没有任何Auth::user(),所以我对用户实例的来源感到困惑。

【问题讨论】:

  • $user 只是变量的名称。它也可以是$biggieSmalls 或其他任何东西。该变量只包含UserRepository 的一个实例。
  • Laravel 的 DIC 创建它是因为控制器需要一个。
  • @devk 该实例在哪里分配给变量?我知道它可以被命名为任何东西,但我不明白它的分配位置。它不在类型提示中是吗?
  • 啊,好吧,对不起。通常你将它们绑定在服务提供者中(假设你想将实现绑定到接口,你会在服务提供者中进行)。但是 Laravel 也有automatic 分辨率。无论如何,jfadich 解释得更好。

标签: php laravel laravel-5


【解决方案1】:

在 Laravel 中,依赖注入由 Container 处理。我正在简化,但您可以将容器视为对象的来源。如果有单例,则将其存储在容器中。否则容器知道如何为你实例化对象。每当 Laravel 调用方法(如在控制器中)或为您实例化对象时,它都会检查构造函数并查找类型提示的依赖项。如果它看到一个依赖,它知道如何检索或创建它,它会这样做并为你传递它。

所以当 Laravel 实例化控制器时,它会查看构造函数

public function __construct(UserRepository $user)
{
    $this->user = $user;
}

容器使用类型提示来查看它需要UserRepository,因此它将为您实例化一个新的。它也递归地执行此操作。因此,当它创建一个新的 UserRepository 时,它会查看该构造函数并发现它需要一个 RoleRepository,因此它也会实例化它。

TLDR:服务容器检查您的依赖项并为您实例化它们。

【讨论】:

  • 好吧,这是有道理的。然后看起来存储库根据正在传递的电子邮件确定它是哪个用户,不明白为什么它不只使用Auth::user();,但我想这是我的下一个任务!谢谢你:)
  • 注入需要使用 setter 配置的类怎么样。是否有与 zend 框架 2.4 相同的工厂模式。谢谢
  • @xiarnousx 我不熟悉 Zend 框架,所以我不确定它的比较,但你可以定义一个对象应该如何由 binding it to the container 构造然后 Laravel 将使用该闭包来构建当您请求注入对象时。
  • @jfadich 谢谢,所以基本上我们在 AppServiceProviderService 启动方法中绑定。正确的?或者创建我们自己的服务提供者并将其注册到内核中。我还没有实验过。谢谢。
  • @xiarnousx 这些地方都可以。哪个合适取决于项目。如果它是一次性对象,那么 AppServiceProvider 就可以了。如果它是一个具有大量绑定的大型项目,那么为应用程序的每个部分创建一个自定义服务提供程序以保持代码清洁和分离可能是一个好主意。无论您在哪个服务提供商中进行绑定,功能上都是相同的。
【解决方案2】:

欢迎来到 Laravel 的可疑魔法。这些依赖注入的基本思想是,根据你定义路由和控制器的方式,Laravel 可以执行一些 url 的自动解析、这些 url 中的 id 识别以及对象的数据库获取。

我的问题是我不明白 $user 来自哪里。

您可能应该阅读docs on the service container。您还可以使用以下命令更好地了解您的路由定义如何转换为带有参数的 url:

php artisan route:list

在我的一个项目中,结果如下:

+--------+-----------+----------------------------+--------------------+-------------------------------------------------+--------------+
| Domain | Method    | URI                        | Name               | Action                                          | Middleware   |
+--------+-----------+----------------------------+--------------------+-------------------------------------------------+--------------+
|        | GET|HEAD  | /                          |                    | Closure                                         | web          |
|        | GET|HEAD  | api/user                   |                    | Closure                                         | api,auth:api |
|        | GET|HEAD  | categories                 | categories.index   | App\Http\Controllers\CategoryController@index   | web          |
|        | POST      | categories                 | categories.store   | App\Http\Controllers\CategoryController@store   | web          |
|        | GET|HEAD  | categories/create          | categories.create  | App\Http\Controllers\CategoryController@create  | web          |
|        | GET|HEAD  | categories/{category}      | categories.show    | App\Http\Controllers\CategoryController@show    | web          |
|        | PUT|PATCH | categories/{category}      | categories.update  | App\Http\Controllers\CategoryController@update  | web          |
|        | DELETE    | categories/{category}      | categories.destroy | App\Http\Controllers\CategoryController@destroy | web          |
|        | GET|HEAD  | categories/{category}/edit | categories.edit    | App\Http\Controllers\CategoryController@edit    | web          |
|        | GET|HEAD  | products                   | products.index     | App\Http\Controllers\ProductController@index    | web          |
|        | POST      | products                   | products.store     | App\Http\Controllers\ProductController@store    | web          |
|        | GET|HEAD  | products/create            | products.create    | App\Http\Controllers\ProductController@create   | web          |
|        | GET|HEAD  | products/{product}         | products.show      | App\Http\Controllers\ProductController@show     | web          |
|        | PUT|PATCH | products/{product}         | products.update    | App\Http\Controllers\ProductController@update   | web          |
|        | DELETE    | products/{product}         | products.destroy   | App\Http\Controllers\ProductController@destroy  | web          |
|        | GET|HEAD  | products/{product}/edit    | products.edit      | App\Http\Controllers\ProductController@edit     | web          |
+--------+-----------+----------------------------+--------------------+-------------------------------------------------+--------------+

所有这些路由及其 uri 和参数仅由几个非常简单的路由定义生成。这是我的路线文件:

$ cat routes/web.php
<?php

Route::get('/', function () {
    return view('master');
});

Route::resource('products', 'ProductController');
Route::resource('categories', 'CategoryController');

如果您查看上述路由输出中的 URI 列表,您会看到在 URI 中命名的参数,例如 {category}{product}。这些对应于 Laravel 标识的 URI 中的 ids/keys。 Laravel 足够“聪明”,可以查看我的 Controller 文件,查看各种函数中的类型提示,并检测到我的函数需要注入依赖项。

例如,Category 控制器的 show 方法如下所示:

public function show(Tree $category)
{
    var_dump($category);
}

我的控制器可能看起来有点不寻常,因为我在输入提示我想要一个 Tree 类型的对象,但是 Laravel 足够聪明地认识到我确实想要一个 Tree 类型的模型,所以它解析出 url并在其中找到 id 并自动获取我的数据库表 trees 中的记录,其中 id 与我的 url 的 {category} 片段匹配,并将其注入我的函数中。

请注意,当我尝试将输入参数命名为 $tree 而不是 $category 时,我 had some trouble。其他线程也可能有助于回答您的问题。

最重要的是,Laravel 做了很多“魔法”,希望您能从繁琐的手动定义自己的代码和查询以检索您想要的对象中解脱出来。

【讨论】: