Laravel 13 服务提供者是应用引导的核心,本文深入探讨高级用法。

服务提供者基础

基础服务提供者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

abstract class BaseServiceProvider extends ServiceProvider
{
protected bool $defer = false;

protected array $bindings = [];

protected array $singletons = [];

public function register(): void
{
foreach ($this->bindings as $abstract => $concrete) {
$this->app->bind($abstract, $concrete);
}

foreach ($this->singletons as $abstract => $concrete) {
$this->app->singleton($abstract, $concrete);
}

$this->registerServices();
}

public function boot(): void
{
$this->bootServices();
}

protected function registerServices(): void
{
}

protected function bootServices(): void
{
}

public function provides(): array
{
return array_keys(array_merge($this->bindings, $this->singletons));
}
}

延迟加载

延迟服务提供者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?php

namespace App\Providers;

use App\Contracts\PaymentInterface;
use App\Services\PaymentService;
use Illuminate\Support\ServiceProvider;

class DeferredPaymentServiceProvider extends ServiceProvider
{
protected bool $defer = true;

public function register(): void
{
$this->app->singleton(PaymentInterface::class, function ($app) {
return new PaymentService(
config('services.payment.key'),
config('services.payment.secret')
);
});

$this->app->singleton('payment', function ($app) {
return $app->make(PaymentInterface::class);
});
}

public function provides(): array
{
return [
PaymentInterface::class,
'payment',
];
}
}

class DeferredCacheServiceProvider extends ServiceProvider
{
protected bool $defer = true;

public function register(): void
{
$this->app->singleton('cache', function ($app) {
return new CacheManager($app);
});

$this->app->singleton('cache.store', function ($app) {
return $app['cache']->driver();
});

$this->app->singleton('cache.psr6', function ($app) {
return new Psr16Adapter($app['cache.store']);
});
}

public function provides(): array
{
return ['cache', 'cache.store', 'cache.psr6'];
}
}

配置发布

发布配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class PackageServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->publishes([
__DIR__ . '/../config/package.php' => config_path('package.php'),
], 'config');

$this->publishes([
__DIR__ . '/../resources/views' => resource_path('views/vendor/package'),
], 'views');

$this->publishes([
__DIR__ . '/../database/migrations' => database_path('migrations'),
], 'migrations');

$this->publishes([
__DIR__ . '/../public' => public_path('vendor/package'),
], 'public');

$this->publishes([
__DIR__ . '/../config/package.php' => config_path('package.php'),
__DIR__ . '/../resources/views' => resource_path('views/vendor/package'),
], 'package-all');
}

public function register(): void
{
$this->mergeConfigFrom(
__DIR__ . '/../config/package.php',
'package'
);

$this->loadViewsFrom(
__DIR__ . '/../resources/views',
'package'
);

$this->loadTranslationsFrom(
__DIR__ . '/../resources/lang',
'package'
);

$this->loadMigrationsFrom(
__DIR__ . '/../database/migrations'
);

$this->loadRoutesFrom(
__DIR__ . '/../routes/web.php'
);
}
}

命令注册

注册命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php

namespace App\Providers;

use App\Console\Commands\ProcessQueueCommand;
use App\Console\Commands\CleanupCommand;
use App\Console\Commands\ReportCommand;
use Illuminate\Support\ServiceProvider;

class CommandServiceProvider extends ServiceProvider
{
protected array $commands = [
ProcessQueueCommand::class,
CleanupCommand::class,
ReportCommand::class,
];

public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->commands($this->commands);

$this->publishes([
__DIR__ . '/../../stubs' => base_path('stubs'),
], 'stubs');
}
}

public function register(): void
{
$this->app->singleton('command.process-queue', function ($app) {
return new ProcessQueueCommand(
$app->make('queue'),
$app->make('log')
);
});

$this->app->singleton('command.cleanup', function ($app) {
return new CleanupCommand(
$app->make('filesystem')
);
});
}

public function provides(): array
{
return [
'command.process-queue',
'command.cleanup',
];
}
}

中间件注册

中间件服务提供者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?php

namespace App\Providers;

use App\Http\Middleware\Authenticate;
use App\Http\Middleware\Authorize;
use App\Http\Middleware\ThrottleRequests;
use Illuminate\Support\ServiceProvider;

class MiddlewareServiceProvider extends ServiceProvider
{
protected array $middleware = [
Authenticate::class,
Authorize::class,
ThrottleRequests::class,
];

protected array $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
],

'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];

protected array $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];

public function boot(): void
{
$router = $this->app['router'];

foreach ($this->middleware as $middleware) {
$router->middleware($middleware);
}

foreach ($this->middlewareGroups as $group => $middlewares) {
$router->middlewareGroup($group, $middlewares);
}

foreach ($this->routeMiddleware as $alias => $middleware) {
$router->aliasMiddleware($alias, $middleware);
}
}
}

事件监听

事件服务提供者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php

namespace App\Providers;

use App\Events\OrderCreated;
use App\Events\UserRegistered;
use App\Listeners\SendOrderConfirmation;
use App\Listeners\SendWelcomeEmail;
use App\Listeners\UpdateStatistics;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
protected $listen = [
UserRegistered::class => [
SendWelcomeEmail::class,
UpdateStatistics::class,
],

OrderCreated::class => [
SendOrderConfirmation::class,
UpdateInventory::class,
'App\Listeners\NotifyAdmin@handle',
],
];

protected $subscribe = [
'App\Listeners\UserEventSubscriber',
'App\Listeners\OrderEventSubscriber',
];

protected $observers = [
User::class => [UserObserver::class],
Order::class => [OrderObserver::class],
];

public function boot(): void
{
parent::boot();

foreach ($this->observers as $model => $observers) {
$model::observe($observers);
}
}
}

路由注册

路由服务提供者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
protected string $namespace = 'App\\Http\\Controllers';

protected array $apiRoutes = [
'v1' => __DIR__ . '/../../routes/api/v1.php',
'v2' => __DIR__ . '/../../routes/api/v2.php',
];

public function boot(): void
{
parent::boot();

Route::pattern('id', '[0-9]+');
Route::pattern('slug', '[a-z0-9-]+');
Route::pattern('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}');
}

public function map(): void
{
$this->mapApiRoutes();

$this->mapWebRoutes();

$this->mapAdminRoutes();
}

protected function mapWebRoutes(): void
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}

protected function mapApiRoutes(): void
{
foreach ($this->apiRoutes as $version => $path) {
Route::prefix("api/{$version}")
->middleware(['api', "api.version:{$version}"])
->namespace("{$this->namespace}\\Api\\{$version}")
->group($path);
}
}

protected function mapAdminRoutes(): void
{
Route::prefix('admin')
->middleware(['web', 'auth', 'admin'])
->namespace("{$this->namespace}\\Admin")
->group(base_path('routes/admin.php'));
}
}

视图组件

视图服务提供者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php

namespace App\Providers;

use App\View\Components\Alert;
use App\View\Components\Card;
use App\View\Components\Modal;
use App\View\Composers\NavigationComposer;
use App\View\Composers\UserComposer;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade;

class ViewServiceProvider extends ServiceProvider
{
protected array $components = [
'alert' => Alert::class,
'card' => Card::class,
'modal' => Modal::class,
];

protected array $composers = [
'layouts.*' => NavigationComposer::class,
'users.*' => UserComposer::class,
];

public function boot(): void
{
foreach ($this->components as $alias => $component) {
Blade::component($alias, $component);
}

foreach ($this->composers as $views => $composer) {
view()->composer($views, $composer);
}

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

Blade::directive('money', function ($expression) {
return "<?php echo number_format($expression, 2); ?>";
});

Blade::if('admin', function () {
return auth()->check() && auth()->user()->isAdmin();
});

Blade::if('feature', function ($feature) {
return app('features')->enabled($feature);
});
}
}

验证规则

验证服务提供者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

namespace App\Providers;

use App\Rules\PhoneNumber;
use App\Rules\PostalCode;
use App\Rules\StrongPassword;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;

class ValidationServiceProvider extends ServiceProvider
{
protected array $rules = [
'phone' => PhoneNumber::class,
'postal_code' => PostalCode::class,
'strong_password' => StrongPassword::class,
];

public function boot(): void
{
foreach ($this->rules as $name => $rule) {
Validator::extend($name, function ($attribute, $value, $parameters, $validator) use ($rule) {
return (new $rule)->passes($attribute, $value);
});
}

Validator::extend('foo', function ($attribute, $value, $parameters, $validator) {
return $value === 'foo';
});

Validator::replacer('foo', function ($message, $attribute, $rule, $parameters) {
return str_replace(':attribute', $attribute, 'The :attribute must be foo');
});

Validator::extendImplicit('required_if_admin', function ($attribute, $value, $parameters, $validator) {
if (auth()->check() && auth()->user()->isAdmin()) {
return !empty($value);
}
return true;
});
}
}

总结

Laravel 13 服务提供者是应用引导的核心机制。通过延迟加载、配置发布、命令注册、中间件注册等功能,可以构建模块化、可维护的应用架构。