Laravel 控制器依赖注入

13

我正在尝试在Laravel中进行依赖注入,以使我的控制器和模型尽可能精简。目标是拥有存储库来处理与特定模型相关的数据提取。

为此,我正在尝试遵循文档(这里)和流行的Laravel样板(这里)的示例。

但我不明白$user从哪里来。

因此,查看样板我们有两个文件:

ProfileController(这里)

以下是摘录:

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(),所以我不知道用户实例是从哪里来的。


1
$user 只是变量的名称。它也可以是 $biggieSmalls 或其他任何名称。该变量仅保存 UserRepository 的实例。 - DevK
Laravel的依赖注入容器是因为控制器需要一个而创建的。 - bassxzero
@devk 那个实例赋值给哪个变量了?我知道它可以被命名为任何东西,但我不知道它被赋值在哪里。它不是在类型提示中吗? - Summer Developer
2
啊,好的,抱歉。通常你可以在服务提供商中绑定它们(比如说你想要将实现绑定到接口,你可以在服务提供商中完成)。但 Laravel 也有自动解析功能。无论如何,jfadich 解释得更好。 - DevK
2个回答

17
在 Laravel 中,依赖注入是由容器(Container)处理的。简单来说,你可以把容器看作是对象的来源。如果有一个单例(singleton),它会被存储在容器中。否则,容器会知道如何为你实例化对象。每当 Laravel 调用一个方法(比如控制器中的方法)或为你实例化对象时,它都会检查构造函数并查找类型提示的依赖项。如果它看到一个它知道如何检索或创建的依赖项,它将这样做并将其传递给你。
因此,当 Laravel 实例化控制器时,它会查看构造函数。
public function __construct(UserRepository $user)
{
    $this->user = $user;
}

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

简而言之:服务容器检查您的依赖项,并将为您实例化它们。


好的,那很有道理。然后看起来存储库是根据传递的电子邮件找出用户身份,不明白为什么它不直接使用 Auth::user(); ,但我猜这就是我的下一个任务!谢谢 :) - Summer Developer
注入需要使用setter进行配置的类怎么办?是否有与Zend Framework 2.4相同的工厂模式?谢谢。 - xiarnousx
@xiarnousx 我不熟悉Zend框架,所以我不确定它如何比较,但您可以通过将其绑定到容器来定义对象的构建方式。然后当您请求注入时,Laravel会使用该闭包来构建对象。 - jfadich
@jfadich 谢谢,所以我们基本上是在AppServiceProviderService的启动方法中绑定。对吧?或者创建我们自己的服务提供程序并在内核中注册它。我还没有尝试过。谢谢。 - xiarnousx
@xiarnousx 任何一个地方都可以。哪个适合取决于项目。如果它是一个一次性的对象,那么AppServiceProvider就足够了。如果是一个大型项目,有很多绑定,为每个应用程序部分创建自定义服务提供程序可能是一个好主意,以保持代码干净和分离。无论在哪个服务提供程序中进行绑定,功能上都是相同的。 - jfadich

7
欢迎来到 Laravel 的神奇世界。这些依赖注入的基本思想是,根据您如何定义路由和控制器,Laravel 可以自动解析 URL、识别这些 URL 中的 ID,并从数据库中获取对象。

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

您应该阅读服务容器文档,这样可以更好地了解路由定义如何转换为带参数的 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}的参数。这些对应于URI中的id/键,Laravel可以识别。Laravel足够聪明,它会查看我的Controller文件,看到各个函数中的类型提示,并检测出我的函数需要注入一个依赖项。例如,Category控制器的show方法如下:
public function show(Tree $category)
{
    var_dump($category);
}

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

请注意,当我尝试将输入参数命名为$tree而不是$category时,我遇到了一些麻烦。那个线程可能也会在一定程度上帮助回答您的问题。

总之,Laravel做了很多"魔法",希望能使您从手动定义自己的代码和查询以检索所需对象的单调乏味中解放出来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接