Laravel 13 依赖注入进阶深度解析

依赖注入是 Laravel 框架的核心特性之一,它实现了控制反转(IoC)设计原则。本文将深入探讨 Laravel 13 中依赖注入的高级用法。

依赖注入基础回顾

什么是依赖注入

依赖注入是一种设计模式,它将依赖关系从类内部移到外部,由容器负责注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

namespace App\Services;

use App\Contracts\LoggerInterface;

class UserService
{
public function __construct(
protected LoggerInterface $logger
) {}

public function createUser(array $data): User
{
$this->logger->info('Creating user', $data);
return User::create($data);
}
}

构造函数注入

基本用法

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
<?php

namespace App\Services;

use App\Contracts\Repositories\UserRepositoryInterface;
use App\Contracts\Services\NotificationServiceInterface;
use App\Contracts\Services\CacheServiceInterface;

class UserService
{
public function __construct(
protected UserRepositoryInterface $users,
protected NotificationServiceInterface $notifications,
protected CacheServiceInterface $cache
) {}

public function find(int $id): ?User
{
return $this->cache->remember("user.{$id}", 3600, function () use ($id) {
return $this->users->find($id);
});
}

public function create(array $data): User
{
$user = $this->users->create($data);

$this->notifications->send(
$user->email,
'Welcome!',
'emails.welcome'
);

return $user;
}
}

可选依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

namespace App\Services;

use App\Contracts\LoggerInterface;

class PaymentService
{
protected ?LoggerInterface $logger;

public function __construct(?LoggerInterface $logger = null)
{
$this->logger = $logger;
}

public function process(float $amount): bool
{
$this->logger?->info("Processing payment: {$amount}");

return true;
}
}

方法注入

控制器方法注入

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
<?php

namespace App\Http\Controllers;

use App\Models\User;
use App\Services\UserService;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;

class UserController extends Controller
{
public function index(Request $request, UserService $service): JsonResponse
{
$users = $service->paginate(
$request->input('per_page', 15)
);

return response()->json($users);
}

public function show(int $id, UserService $service): JsonResponse
{
$user = $service->find($id);

if (!$user) {
return response()->json(['error' => 'User not found'], 404);
}

return response()->json($user);
}

public function store(Request $request, UserService $service): JsonResponse
{
$user = $service->create($request->validated());

return response()->json($user, 201);
}
}

中间件方法注入

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
<?php

namespace App\Http\Middleware;

use App\Services\RateLimiterService;
use Closure;
use Illuminate\Http\Request;

class RateLimitMiddleware
{
public function handle(
Request $request,
Closure $next,
RateLimiterService $limiter,
int $maxAttempts = 60
) {
$key = $request->ip();

if (!$limiter->attempt($key, $maxAttempts)) {
return response()->json(['error' => 'Too many requests'], 429);
}

return $next($request);
}
}

接口绑定

简单绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Contracts\LoggerInterface;
use App\Services\FileLogger;
use App\Contracts\CacheInterface;
use App\Services\RedisCache;

class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->bind(LoggerInterface::class, FileLogger::class);

$this->app->bind(CacheInterface::class, function ($app) {
return new RedisCache(
config('cache.stores.redis')
);
});
}
}

单例绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Contracts\ConfigurationInterface;
use App\Services\ConfigurationService;

class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(ConfigurationInterface::class, function ($app) {
return new ConfigurationService(
config('app'),
$app->make('cache.store')
);
});
}
}

实例绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\ApiClient;

class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->instance('api.client', new ApiClient(
config('services.api.base_url'),
config('services.api.key')
));
}
}

上下文绑定

简单上下文绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Contracts\LoggerInterface;
use App\Loggers\FileLogger;
use App\Loggers\DatabaseLogger;
use App\Services\PaymentService;
use App\Services\AuditService;

class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->when(PaymentService::class)
->needs(LoggerInterface::class)
->give(FileLogger::class);

$this->app->when(AuditService::class)
->needs(LoggerInterface::class)
->give(DatabaseLogger::class);
}
}

复杂上下文绑定

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
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Contracts\DatabaseInterface;
use App\Contracts\CacheInterface;
use App\Databases\ReadDatabase;
use App\Databases\WriteDatabase;
use App\Services\ReportService;
use App\Services\AnalyticsService;

class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->when(ReportService::class)
->needs(DatabaseInterface::class)
->give(function ($app) {
return $app->make(ReadDatabase::class);
});

$this->app->when(AnalyticsService::class)
->needs(DatabaseInterface::class)
->give(function ($app) {
return $app->make(WriteDatabase::class);
});

$this->app->when(ReportService::class)
->needs(CacheInterface::class)
->give(function ($app) {
return $app->make('cache.store')->getStore();
});
}
}

标签绑定

定义标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Validators\EmailValidator;
use App\Validators\PhoneValidator;
use App\Validators\AddressValidator;

class ValidatorServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->tag([
EmailValidator::class,
PhoneValidator::class,
AddressValidator::class,
], 'validators');
}
}

使用标签

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
<?php

namespace App\Services;

use Illuminate\Container\Container;

class ValidationService
{
protected array $validators;

public function __construct(Container $container)
{
$this->validators = $container->tagged('validators');
}

public function validate(array $data): array
{
$errors = [];

foreach ($this->validators as $validator) {
$result = $validator->validate($data);

if (!$result->isValid()) {
$errors = array_merge($errors, $result->getErrors());
}
}

return $errors;
}
}

自动解析

零配置解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

namespace App\Services;

use App\Repositories\UserRepository;

class UserService
{
public function __construct(
protected UserRepository $repository
) {}

public function find(int $id): ?User
{
return $this->repository->find($id);
}
}

$service = app(UserService::class);

手动解析

使用 app() 辅助函数

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
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Services\ReportService;

class GenerateReportCommand extends Command
{
protected $signature = 'report:generate {type}';

public function handle(): int
{
$type = $this->argument('type');

$service = app(ReportService::class, [
'type' => $type,
]);

$report = $service->generate();

$this->info("Report generated: {$report->name}");

return 0;
}
}

使用 make() 方法

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
<?php

namespace App\Services;

use Illuminate\Container\Container;
use App\Contracts\ParserInterface;

class DataProcessor
{
protected Container $container;

public function __construct(Container $container)
{
$this->container = $container;
}

public function process(string $format, string $data): array
{
$parserClass = $this->getParserClass($format);

$parser = $this->container->make($parserClass);

return $parser->parse($data);
}

protected function getParserClass(string $format): string
{
return match ($format) {
'json' => \App\Parsers\JsonParser::class,
'xml' => \App\Parsers\XmlParser::class,
'csv' => \App\Parsers\CsvParser::class,
default => throw new \InvalidArgumentException("Unknown format: {$format}"),
};
}
}

绑定原始值

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
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\ApiClient;

class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->when(ApiClient::class)
->needs('$baseUrl')
->give(config('services.api.base_url'));

$this->app->when(ApiClient::class)
->needs('$timeout')
->give(30);

$this->app->when(ApiClient::class)
->needs('$retries')
->give(function () {
return config('services.api.retries', 3);
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
<?php

namespace App\Services;

class ApiClient
{
public function __construct(
protected string $baseUrl,
protected int $timeout,
protected int $retries
) {}
}

延迟绑定

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
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Contracts\HeavyServiceInterface;

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

public function register(): void
{
$this->app->singleton(HeavyServiceInterface::class, function ($app) {
return new \App\Services\HeavyService(
$app->make('config')
);
});
}

public function provides(): array
{
return [HeavyServiceInterface::class];
}
}

容器事件

解析事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\UserService;

class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->app->resolving(UserService::class, function ($service, $app) {
$service->setResolvedAt(now());
});

$this->app->resolving(function ($object, $app) {
if (method_exists($object, 'setContainer')) {
$object->setContainer($app);
}
});
}
}

绑定事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Contracts\LoggerInterface;

class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->bind(LoggerInterface::class, function ($app) {
$logger = new \App\Services\FileLogger(
storage_path('logs/app.log')
);

$app->terminating(function () use ($logger) {
$logger->flush();
});

return $logger;
});
}
}

测试依赖注入

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
<?php

namespace Tests\Unit\Services;

use Tests\TestCase;
use App\Services\UserService;
use App\Contracts\Repositories\UserRepositoryInterface;
use Mockery;

class UserServiceTest extends TestCase
{
public function test_find_user(): void
{
$mockRepository = Mockery::mock(UserRepositoryInterface::class);
$mockRepository->shouldReceive('find')
->with(1)
->once()
->andReturn((object)['id' => 1, 'name' => 'John']);

$this->app->instance(UserRepositoryInterface::class, $mockRepository);

$service = $this->app->make(UserService::class);
$user = $service->find(1);

$this->assertEquals(1, $user->id);
$this->assertEquals('John', $user->name);
}

public function test_contextual_binding(): void
{
$this->app->when(UserService::class)
->needs('$defaultPerPage')
->give(25);

$service = $this->app->make(UserService::class);

$this->assertEquals(25, $service->getDefaultPerPage());
}
}

最佳实践

1. 依赖接口而非实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

class GoodService
{
public function __construct(
protected LoggerInterface $logger
) {}
}

class BadService
{
public function __construct(
protected FileLogger $logger
) {}
}

2. 避免服务定位器模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

class GoodService
{
public function __construct(
protected UserRepositoryInterface $users
) {}
}

class BadService
{
public function find(int $id)
{
return app(UserRepositoryInterface::class)->find($id);
}
}

3. 使用类型提示

1
2
3
4
5
6
7
8
9
10
<?php

class TypedService
{
public function __construct(
protected UserRepositoryInterface $users,
protected CacheInterface $cache,
protected LoggerInterface $logger
) {}
}

总结

Laravel 13 的依赖注入提供了强大的功能来管理类之间的依赖关系。通过合理使用依赖注入,可以创建松耦合、可测试、可维护的应用程序代码。