Laravel 中的服务提供者:Service Providers 是什么以及如何使用

Laravel

对于那些没有经常在 Laravel 中使用 Service Providers 的人来说,这是一个神秘的「术语」:他们实际上「提供」了哪些「服务」,它们究竟是如何工作的?我将在这篇文章中解释它。


默认 Laravel 服务提供者

让我们从 Laravel 中包含的默认服务提供者开始,它们都在 app/Providers 文件夹中:

  • AppServiceProvider
  • AuthServiceProvider
  • BroadcastServiceProvider
  • EventServiceProvider
  • RouteServiceProvider

它们都是 PHP 类,每个都与它的主题相关:通用「应用程序」、Auth、广播、事件和路由。

并且它们都有一个共同点:boot() 方法。

在该方法内部,你可以编写与这些部分之一相关的任何代码:身份验证、事件、路由等。换句话说,服务提供者只是注册一些全局功能的类。

它们被分离为「Providers」,因为它们在应用程序生命周期的早期执行, 所以这很方便来调用一些全局方法,在执行脚本还没到达模型或控制器之前。

RouteServiceProvider 中有很好的例子,我们来看看它的代码:

class RouteServiceProvider extends ServiceProvider
{
    public const HOME = '/dashboard';

    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::prefix('api')
                ->middleware('api')
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->group(base_path('routes/web.php'));
        });
    }

    protected function configureRateLimiting()
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });
    }
}

这是配置路由文件的类,默认包含routes/web.phproutes/api.php。 请注意,API 的配置不同:路由和中间件为 api 用于是它所用的。

你可以随意去编辑这些服务提供者为你想要的,它们并不在 vendor 文件夹。
一个典型的例子,当你有很多路由想将它们拆分成自定义路由文件时:创建一个 routes/auth.php 路由文件,然后在 RouteServiceProviderboot() 方法中「启用」该文件,只需添加第三行代码:

Route::middleware('web') // 或者你想要添加其他中间件?
    ->group(base_path('routes/auth.php'));

其他的默认提供者的功能请自行探索。除了 AppServiceProvider 之外,它是空的,就像一个占位符,供我们添加与某些全局应用程序设置相关的任何代码。

一个常用的案例是,向 AppServiceProvider 添加 禁用懒加载。 仅需 添加两行代码boot() 方法:

// app/Providers/AppServiceProvider.php
use Illuminate\Database\Eloquent\Model;

public function boot()
{
    Model::preventLazyLoading(! $this->app->isProduction());
}

如果某些关系模型没有预先加载,这将引发异常,这会导致所谓的 N+1 查询性能问题。


什么时候服务提供者被执行?

如果你看过 请求生命周期,这些是最开始执行的操作:

  • public/index.php
  • bootstrap/app.php
  • app/Http/Kernel.php 还有中间件
  • Service Providers: 正是我们这篇文章的主题

config/app.php 文件的 providers 数组中决定了那些服务提供者被加载:

return [

    // ... other configuration values

    'providers' => [

        /*
         * Laravel 框架 Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,

        // ... 来自 /vendor 其他框架提供者 
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

        /*
         * 公共 服务提供者 - 我们上面提到的
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

    ],

];

如你所见, 有来自/vendor文件夹的非公共服务提供商列表,你不应该触碰或编辑它们。 我们感兴趣的那些是在底部,默认情况下,broadcastServicerProvider禁用,可能是因为很少使用。

所有这些服务提供商都是从上到下执行的,迭代该列表两次

第一次迭代是寻找可选方法register(),该方法可用于在启动boot()方法之前启动某些内容。我没有使用过它的经验。

然后,第二次迭代执行所有提供程序的boot()方法。同样,从providers数组的顶部到底部。

再然后,在处理完所有服务提供商之后,Laravel开始解析路由、执行控制器、使用模型等。


创建你自己的服务提供者

除了现有的默认文件之外,你还可以轻松创建服务提供者,实现一些其他功能,如 auth/event/routes。

一个典型的例子是与 Blade 视图相关的配置。如果你想创建 Blade 指令,你可以将该代码添加到任何服务提供者的 boot() 方法中,包括默认的AppServiceProvider,但开发人员通常会创建一个单独的 ViewServiceProvider。

你可以使用以下命令生成它:

php artisan make:provider ViewServiceProvider

它将生成默认模板:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class ViewServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

你可以删除 register() 方法,并在 boot() 内添加 Blade 指令代码:

use Illuminate\Support\Facades\Blade;

public function boot()
{
    Blade::directive('datetime', function ($expression) {
        return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
    });
}

ViewServiceProvider的另一个示例是关于视图组合器,这是[来自官方Laravel文档]的片段](learnku.com/docs/laravel/9.x/views...):

use App\View\Composers\ProfileComposer;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class ViewServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // 使用基于类的组合器......
        View::composer('profile', ProfileComposer::class);

        // 使用基于关闭的组合器....
        View::composer('dashboard', function ($view) {
            //
        });
    }
}

要执行,应将此新提供程序添加到config / app.php中的提供程序数组中,如上所述:

return [
    // ... 其他配置值

    'providers' => [

        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

        // 在此添加你的提供者
        App\Providers\ViewServiceProvider::class,
    ],
];

开源项目的示例

最后,我想提到一些来自免费的Laravel项目的例子

1. spatie/freek.dev: BladeComponentServiceProvider

一家知名公司Spatie已经在Freek Van der Herten的个人博客上发布了源代码, 其中包含这个文件。

app/Providers/BladeComponentServiceProvider.php:

namespace App\Providers;

use App\Http\Components\AdComponent;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;

class BladeComponentServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Blade::component('ad', AdComponent::class);

        Blade::component('front.components.inputField', 'input-field');
        Blade::component('front.components.submitButton', 'submit-button');
        Blade::component('front.components.textarea', 'textarea');
        Blade::component('front.components.textarea', 'textarea');
        Blade::component('front.components.shareButton', 'share-button');
        Blade::component('front.components.lazy', 'lazy');
        Blade::component('front.components.postHeader', 'post-header');

        Blade::component('front.layouts.app', 'app-layout');
    }
}

在 Github 查看源码

2. monicahq/monica: MacroServiceProvider

最受欢迎的 Laravel 开源项目之一,使用有一个单独的文件来注册 Collection 宏:
app/Providers/MacroServiceProvider.php:

namespace App\Providers;

use App\Helpers\CollectionHelper;
use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;

class MacroServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        if (! Collection::hasMacro('sortByCollator')) {
            Collection::macro('sortByCollator', function ($callback, $options = \Collator::SORT_STRING, $descending = false) {
                /** @var Collection */
                $collect = $this;

                return CollectionHelper::sortByCollator($collect, $callback, $options, $descending);
            });
        }

        if (! Collection::hasMacro('groupByItemsProperty')) {
            Collection::macro('groupByItemsProperty', function ($property) {
                /** @var Collection */
                $collect = $this;

                return CollectionHelper::groupByItemsProperty($collect, $property);
            });
        }

        if (! Collection::hasMacro('mapUuid')) {
            Collection::macro('mapUuid', function () {
                /** @var Collection */
                $collect = $this;

                return $collect->map(function ($item) {
                    return $item->uuid;
                })->toArray();
            });
        }
    }
}

查看Github上的源代码

3. phpreel/phpreelcms: DashboardComponentsServiceProvider

Laravel CMS称为Phpreel的Laravel CMS还拥有Blade组件的服务提供商,命名更长。

app/Providers/DashboardComponentsServiceProvider.php:

namespace App\Providers;

//[//]: # (Section Separator)

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade;
use App\Helpers\FileUpload\UploadComponents;

class DashboardComponentsServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        Blade::directive('uploadForm', function () {
            $component = UploadComponents::getUploadForm();
            $html = '<?php echo \'' . $component . '\'; ?>';

            return ('<?php echo "' . $component . '"; ?>');
        });
    }
}

查看Github上的源代码

你还可以在我的网站 LaravelExamples.com上找到更多服务提供商的示例。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://laravel-news.com/service-provide...

译文地址:https://learnku.com/laravel/t/67406

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!