【问题标题】:Laravel - do I need a service provider for each of my service containers / custom classes?Laravel - 我的每个服务容器/自定义类都需要服务提供者吗?
【发布时间】:2017-09-21 20:21:01
【问题描述】:

服务容器/提供者的概念可能比我想象的要简单得多,但经过几个小时的阅读,我仍然完全不明白。

我在app/Library 中创建了一个简单的DateFormat 类。在\config\app.php 中为其创建别名后,我可以立即在任何控制器或刀片模板中使用它。

<?php namespace App\Library;

class DateFormat {

    public static function getDate($timestamp){
      // processing the timestamp      
    }

}

我只是创建了一个服务容器吗?如果是,我是否还需要创建服务提供者?图片中的绑定在哪里?

我非常希望能对这个主题有所了解。
谢谢

【问题讨论】:

    标签: php laravel service dependency-injection inversion-of-control


    【解决方案1】:

    没有。您创建的只是您班级的别名。服务提供者是一种绑定特定类的方式,通常与Facade 结合使用。

    别名只是一种使用类的便捷方式,无需每次都导入整个命名空间类。

    例如,如果您有一个类\My\Very\Long\Class\Adapter,您可以在config/app.php 中为它起别名:

    // config/app.php
    <?php
    'aliases' => [
        // a bunch of aliases
        'MyAdapter' => My\Very\Long\Class\Adapter::class,
    ]
    

    现在你可以这样做了:

    <?php
    
    new MyAdapter();
    ... 
    

    代替:

    <?php
    
    use My\Very\Long\Class\Adapter;
    ...
    new Adapter();
    ...
    

    当您想要解决依赖关系时,通常会使用服务提供者,最常见的是通过注入。当您要解析的类需要将参数传递给构造函数或每次都具有通用设置时,这会很有帮助。您可以在 Provider 中执行所有设置。

    这是一个场景:

    您有一个想要与之交互的 API。我们称之为 SuperApi。 SuperAPI 的文档说要创建 SuperApi 类的实例,您必须执行以下操作:

    <?php
    // Some method (a controller or something)
    public function index()
    {
        $superApi = new \SuperApi\Connector($key, $secret);
        return $superApi->getCustomers();
    }
    

    现在,每次您想创建它的实例时,您都必须进行相同的设置(或将其抽象到某个类,但事实仍然是您需要传递 $key$secret给构造函数)。

    如果您要为这个 Connector 类创建一个别名,可能是:

    // config/app.php
    <?php
    'aliases' => [
        // a bunch of aliases
        'SuperApi' => SuperApi\Connector::class,
    ]
    

    因此,使用该别名,您现在可以这样做:

    <?php
    
    // Some method (a controller or something)
    public function index()
    {
        $superApi = new SuperApi($key, $secret);
        return $superApi->getCustomers();
    }
    

    但是您看,即使使用别名,您仍然需要传递 $key$secret

    这是服务提供商可以提供帮助的地方。

    // app/Providers/SuperApiProvider.php
    <?php
    
    namespace App\Providers;
    
    use SuperApi\Connector;
    use Illuminate\Support\ServiceProvider;
    
    class SuperApiProvider extends ServiceProvider
    {
        /**
         * Register bindings in the container.
         *
         * @return void
         */
        public function register()
        {
            $this->app->bind('superApiConnector', function ($app) {
                return new ApiConnector($app['config']->get('super-api.key'), $app['config']->get('super-api.secret'));
            });
        }
    }
    
    // app/Providers/SuperApi.php (the Facade)
    <?php
    
    namespace App\Providers;
    
    use Illuminate\Support\Facades\Facade;
    
    class SuperApi extends Facade
    {
        protected static function getFacadeAccessor()
        {
            return 'superApiConnector';
        }
    }
    
    
    // config/super-api.config
    <?php
    
    return [
        'key' => env('SUPER_API_KEY'),
        'secret' => env('SUPER_API_SECRET'),
    ];
    
    // config/app.php
    <?php
    'providers' => [
        // a bunch of providers
        App\Providers\SuperApiProvider::class,
    ]
    

    请注意,您在提供程序中绑定的字符串 ('superApiConnector') 与您从外观返回的字符串相同,外观的类名是您实际调用绑定类的方式,在本例中为 @ 987654338@.

    现在,当你想使用 SuperApi\Connector 类时,你可以这样做:

    <?php
    
    // Some method (a controller or something)
    public function index()
    {
        return SuperApi::getCustomers();
    }
    

    正如我上面所说,提供者真正派上用场的地方是当您想要注入它并让Laravel's IoC Container 自动解析注入的类时:

    <?php
    // Some method (a controller or something)
    public function index(SuperApi $api)
    {
        return $api->getCustomers();
    }
    

    需要明确的是,您不需要服务提供者来利用依赖注入。只要类可以被应用程序解析,它就可以被注入。这意味着您要注入的类的构造函数所采用的任何参数也必须是可自动解析的。

    【讨论】:

    • 谢谢,这是我整晚都在寻找的答案。还有两个小问题: 1. 是不是需要在 app.php 中的 providers 列表中添加 SuperApiProvider 才能使注入工作? 2.在provider中,理论上可以把绑定里面的facade换成:$this-&gt;app-&gt;bind('App\SuperApi\Connector', function ($app) {吗?
    • 第一个问题是。我已将其添加到答案中。就您的第二个问题而言,您肯定可以将类路径作为第一个参数传递并取消外观。如果您这样做了,那么您需要在注入时使用完整的类路径进行导入。或者,您可以创建一个别名并注入它。两者都应该得到正确解决。如果您选择不使用 Facade,则不能这样做:SuperApiConnector::getCustomers() 因为(在我的示例中)该方法实际上不是静态的。
    • 感谢您精心制作的答案,现在所有部分都到位了!祝你今天过得愉快! :)
    • @tptcat,让我们看看return new ApiConnector($app['config']-&gt;get('super-api.key'),这一行。 1) 你从.env 文件中获得了auper api 密钥,对吧? 2)假设用户需要在不同的时间使用不同的键,那你会怎么写呢?
    • @tptcat,在register() 方法中,你使用了return new ApiConnector($app['.... - 你从哪里得到ApiConnector 类?
    猜你喜欢
    • 2017-01-11
    • 2019-05-29
    • 2014-10-22
    • 2014-12-01
    • 2016-03-15
    • 2020-01-19
    • 2015-09-12
    • 2016-01-02
    • 2014-11-09
    相关资源
    最近更新 更多