在 Laravel 容器中覆盖单例

Posted

技术标签:

【中文标题】在 Laravel 容器中覆盖单例【英文标题】:Override Singleton in Laravel Container 【发布时间】:2016-03-29 03:36:15 【问题描述】:

我想知道是否有一种简单的方法可以覆盖 Laravel 框架核心中的单例服务集?

例如我正在尝试使用以下提供程序重写 app:name 命令服务“”:

use Hexavel\Console\AppNameCommand;
use Illuminate\Console\Events\ArtisanStarting;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\ServiceProvider;

class NameCommandProvider extends ServiceProvider

    /**
     * Register any other events for your application.
     *
     * @param  \Illuminate\Contracts\Events\Dispatcher  $events
     * @return void
     */
    public function boot(Dispatcher $events)
    
        $events->listen(ArtisanStarting::class, function ($event) 
            $event->artisan->resolve('command.app.name');
        , -1);
    

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    
        $this->app->singleton('command.app.name', function ($app) 
            return new AppNameCommand($app['composer'], $app['files']);
        );
    

由于进行了广泛的检查,我 100% 一切正常,无论我将我的服务提供者(高于或低于 ConsoleSupportServiceProvider)放在什么顺序,它仍然会在我的自定义命令上加载原始 AppNameCommand。

我已经找到了解决办法,但是如果可能的话,如果将来能了解单例服务的行为会很好吗? (如果这有什么不同的话,这是使用 Laravel 5.2。)

【问题讨论】:

【参考方案1】:

我看了这个案子,似乎不是一件容易的事。如果您在自定义提供程序中使用singleton,它将最终被默认提供程序(延迟一个)覆盖,因此似乎不是这样。

在检查了简单的方法不起作用之后,您需要做的是分析 Laravel 注册此命令时发生的情况。

因此,在您的情况下,您首先搜索command.app.name - 您会看到它在Illuminate\Foundation\Providers\ArtisanServiceProvider 中,并且您可能希望覆盖方法registerAppNameCommand

所以现在您要查找 ArtisanServiceProvider 的出现以查看它的启动位置 - 您会看到它在 $providers 属性中的 Illuminate\Foundation\Providers\ConsoleSupportServiceProvider 中(您可能希望更改它)。

所以最后你应该寻找ConsoleSupportServiceProvider的出现,你会看到它在config/app.php中。

那么在这种情况下你需要做什么:

更改config/app.php - 将Illuminate\Foundation\Providers\ConsoleSupportServiceProvider 更改为您的自定义ConsoleSupportServiceProvider 在您的自定义中,您应该从\Illuminate\Foundation\Providers\ConsoleSupportServiceProvider 扩展,但将$providersIlluminate\Foundation\Providers\ArtisanServiceProvider 更改为您的自定义ArtisanServiceProvider 最终创建自定义ArtisanServiceProvider,它将从\Illuminate\Foundation\Providers\ArtisanServiceProvider 扩展,您使用singleton 中的自定义类覆盖registerAppNameCommand

使用这种方式,您将实现您的目标(我已经验证了自定义类将用于运行命令php artisan app:name)。

或者,您可能希望在您的自定义 ArtisanServiceProvider 中从 $devCommands 中删除 'AppName' => 'command.app.name', 并使用您的自定义服务提供商,如您在注册单身人士时显示的那样,但我没有尝试过这种方法。

【讨论】:

是的,我认为这可以作为替代方法,并且是唯一的方法,但老实说,这是一个非常笨拙的选择,这是一种耻辱。我也可以通过解决它来覆盖命令,例如新命令将替换 app:name 如果其他人不知道 command.app.name 服务是多余的,这可能会导致潜在问题 是的,你是对的,在 Laravel 中有一些功能你需要做很多改变才能使它们工作,但在其他框架中它通常更糟:)【参考方案2】:

实际上有一种更简洁的方法可以做到这一点。你基本上是想扩展一个核心绑定,这可以通过使用extend方法来实现:

$this->app->extend('command.app.name', function ($command, $app) 
    return new AppNameCommand($app['composer'], $app['files']);
);

Jason Lewis 在Tutsplus 上有一篇关于 Laravel 的 IoC 的非常好的文章。一定要检查一下;)

【讨论】:

以上是关于在 Laravel 容器中覆盖单例的主要内容,如果未能解决你的问题,请参考以下文章

laravel Application实例化后两个方法

扩展/覆盖 Laravel 验证器类

Laravel/Lumen 5.3.3:在迁移中覆盖 env 值

在 Laravel 中覆盖关系

Laravel框架中运用单例模式

如何解决 Lumen/Laravel 中的单例?