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