【问题标题】:Injecting an instance bound to the service container注入绑定到服务容器的实例
【发布时间】:2016-01-03 20:25:48
【问题描述】:

我有一个服务提供者,我想用它来将一个类的实例绑定到服务容器:

namespace App\Providers;

use Eluceo\iCal\Component\Calendar;
use Illuminate\Support\ServiceProvider;

class IcalProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->instance('iCal', function () {
            return new Calendar(config('calendar.name'));
        });
    }
}

据我了解the documentation on binding an instance,这允许我将密钥iCal 绑定到服务容器,以便稍后在我的控制器或服务类中我可以键入提示iCal,并且在服务提供者中创建的实例将是用过的。

所以我创建了一个控制器并尝试输入提示我的实例:

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;

class CalendarInviteController extends Controller
{
    public function download(iCal $ical, $sessionId)
    {
        dd($ical);
    }
}

但是当我这样做时,我得到了错误:

类 App\Http\Controllers\iCal 不存在

是有道理的,因为它应用它在控制器命名空间中寻找一个名为iCal 的类,但它并不存在。该实例没有 use 语句,因为 iCal 只是一个文本键,所以我尝试告诉它查看可能修复它的根命名空间:

public function download(\iCal $ical, $sessionId)

我得到了错误:

类 iCal 不存在

当我阅读resolving from the service container 上的文档部分时,看起来我在控制器中唯一需要做的就是输入提示以获取实例。

我误解了文档吗?

更新

我还应该提到我确实将我的服务提供商添加到我的config/app.php 文件中。

另外,当我创建一个接口时,将其绑定到服务容器,编辑供应商代码以实现所述接口,并注入该接口而不是它的工作原理,但这需要我编辑供应商代码,而我没有想。

【问题讨论】:

    标签: php laravel laravel-5 ioc-container laravel-5.1


    【解决方案1】:

    正如您在docs 中看到的,instance 方法需要一个键和一个对象实例以在容器中注册。所以,如果你想在容器中注册一个特定的实例,注册应该是:

    namespace App\Providers;
    
    use Eluceo\iCal\Component\Calendar;
    use Illuminate\Support\ServiceProvider;
    
    class IcalProvider extends ServiceProvider
    {
        public function register()
        {
            //register a specific instance of the Calendar class in the container
            $this->app->instance('iCal', new Calendar(config('calendar.name') );
        }
    }
    

    这样你就可以取回实例:

     $cal = \App::make('iCal');
    

    如果您的目的是在控制器方法中对类进行类型提示,并且您想从服务容器中解析先前注册的实例,您可以这样做:

    namespace App\Providers;
    
    use Eluceo\iCal\Component\Calendar;
    use Illuminate\Support\ServiceProvider;
    
    class IcalProvider extends ServiceProvider
    {
        public function register()
        {
            //the key will be 'Eluceo\iCal\Component\Calendar'
            $this->app->instance( Calendar::class, new Calendar(config('calendar.name') );
        }
    }
    

    现在,在您的控制器中:

    namespace App\Http\Controllers;
    
    //important: specify the Calendar namespace
    use Eluceo\iCal\Component\Calendar;
    
    class CalendarInviteController extends Controller
    {
        public function download(Calendar $ical, $sessionId)
        {
            dd($ical);
        }
    }
    

    这样 Laravel 会看到你想要一个 Calendar 对象,它会尝试从服务容器中获取它,看看是否存在这个键的绑定:(因为这是你在控制器)

    Eluceo\iCal\Component\Calendar
    

    并且绑定存在!由于您已将此密钥绑定到服务提供程序中的服务容器,因此 Laravel 将返回您注册的实例。

    在您提供的代码中,您提示了类 iCal,但该类在任何地方都不存在,因此 Laravel 无法实例化该类

    【讨论】:

    • 这个:$this->app->instance( Calendar::class... 让我大开眼界。我想当我将一个接口绑定到一个具体的实现时,Laravel 本质上是在说“当你请求这个接口时,返回后面的任何内容”。你发布的那行代码让我意识到它实际上是在说“当你要求这个完全限定的类/接口/whatever时,返回下面的内容”。这种差异是巨大的。感谢您指出这一点!
    • 是的,instance方法不是用来绑定接口的,而是服务提供者中的具体对象(好像是一个注册中心)
    【解决方案2】:

    如果您想将依赖项注入您的控制器(这很好,值得称赞!),那么您需要一个接口名称来进行类型提示。

    通常你会有一个通用接口,然后将该接口绑定到一个具体的实现。所以你可能有一个日历服务接口,它绑定到你的 iCal 实现。像这样的:

    use Eluceo\iCal\Component\Calendar;
    
    class CalendarServiceProvider extends ServiceProvider
    {
        public function register()
        {
            $this->app->bind('App\Services\Calendar', function ($app) {
                return new Calendar(config('calendar.name'));
            });
        }
    
        public function provides()
        {
            return ['App\Services\Calendar'];
        }
    }
    

    只要您在 config/app.php 文件中注册您的服务提供商,您现在就可以在类中键入提示您的日历依赖项:

    use App\Services\Calendar;
    
    class InvitationController extends Controller
    {
        protected $calendar;
    
        public function __construct(Calendar $calendar)
        {
            $this->calendar = $calendar;
        }
    }
    

    【讨论】:

    • 谢谢马丁。通常我会创建一个接口,在具体类上实现它,然后绑定它,但我试图绑定的类是通过 composer 加载的第 3 方类,所以我不想在具体类中添加 implements ...在网络作曲家更新期间可能会被覆盖的类。我可以在我的应用程序结构中创建一个扩展供应商类的类,然后在其上实现接口,但这对于一件小事来说似乎是一个很大的工作,这就是我想要使用实例的原因。就是说,你描述的是def我的首选方法。
    • 我想你误解了:你不要随时编辑具体类(在vendor目录中)。您只需将其绑定到您在服务提供者中选择的接口名称。
    • 啊,出于某种原因,我认为必须在具体类上实现接口才能使其工作。很高兴知道服务提供商不需要这样做。
    猜你喜欢
    • 1970-01-01
    • 2020-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多