Laravel 13 门面模式详解
门面(Facades)是 Laravel 提供的一种优雅的静态代理机制,让开发者可以使用简洁的静态语法访问服务容器中的服务。
门面基础
什么是门面
门面为服务容器中的类提供了一个静态接口,让代码更加简洁易读。
1 2 3 4 5 6 7
| use Illuminate\Support\Facades\Cache;
Cache::get('key');
app('cache')->get('key');
|
常用门面
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Validator;
Cache::put('key', 'value', 3600); DB::table('users')->get(); Log::info('Message'); Mail::to($user)->send(new WelcomeEmail());
|
内置门面
Cache 门面
1 2 3 4 5 6 7 8 9 10 11 12 13
| use Illuminate\Support\Facades\Cache;
Cache::put('key', 'value', 3600); Cache::get('key', 'default'); Cache::has('key'); Cache::forget('key'); Cache::flush();
Cache::remember('users', 3600, function () { return User::all(); });
Cache::tags(['users'])->put('active', $users, 3600);
|
DB 门面
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| use Illuminate\Support\Facades\DB;
DB::table('users')->get(); DB::table('users')->where('active', true)->first(); DB::insert('insert into users (name, email) values (?, ?)', ['John', 'john@example.com']);
DB::transaction(function () { DB::table('orders')->insert(['user_id' => 1]); DB::table('products')->decrement('stock'); });
DB::listen(function ($query) { Log::info($query->sql); });
|
Log 门面
1 2 3 4 5 6 7 8 9 10 11 12 13
| use Illuminate\Support\Facades\Log;
Log::emergency($message); Log::alert($message); Log::critical($message); Log::error($message); Log::warning($message); Log::notice($message); Log::info($message); Log::debug($message);
Log::channel('daily')->info('Message'); Log::stack(['single', 'slack'])->info('Message');
|
Route 门面
1 2 3 4 5 6 7 8 9
| use Illuminate\Support\Facades\Route;
Route::get('/users', [UserController::class, 'index']); Route::post('/users', [UserController::class, 'store']); Route::resource('posts', PostController::class);
Route::middleware(['auth'])->group(function () { Route::get('/dashboard', [DashboardController::class, 'index']); });
|
Storage 门面
1 2 3 4 5 6 7 8
| use Illuminate\Support\Facades\Storage;
Storage::put('file.txt', 'Contents'); Storage::get('file.txt'); Storage::exists('file.txt'); Storage::delete('file.txt'); Storage::url('file.txt'); Storage::disk('s3')->put('file.txt', 'Contents');
|
创建自定义门面
创建服务类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?php
namespace App\Services;
class PaymentService { public function process(float $amount): array { return [ 'status' => 'success', 'amount' => $amount, 'transaction_id' => uniqid(), ]; }
public function refund(string $transactionId): bool { return true; } }
|
创建门面类
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Payment extends Facade { protected static function getFacadeAccessor(): string { return 'payment'; } }
|
注册服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider; use App\Services\PaymentService;
class PaymentServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind('payment', function ($app) { return new PaymentService(); }); } }
|
使用门面
1 2 3 4
| use App\Facades\Payment;
$result = Payment::process(100.00); $refunded = Payment::refund('txn_123');
|
实时门面
使用实时门面
1 2 3
| use Facades\App\Services\PaymentService;
$result = PaymentService::process(100.00);
|
实时门面原理
Laravel 会自动将 Facades\ 命名空间下的类转换为对应的服务实例。
门面类参考
Auth 门面
1 2 3 4 5 6 7 8
| use Illuminate\Support\Facades\Auth;
Auth::login($user); Auth::logout(); Auth::user(); Auth::id(); Auth::check(); Auth::attempt(['email' => $email, 'password' => $password]);
|
Config 门面
1 2 3 4 5
| use Illuminate\Support\Facades\Config;
Config::get('app.name'); Config::set('app.locale', 'zh'); Config::has('app.timezone');
|
Event 门面
1 2 3 4 5
| use Illuminate\Support\Facades\Event;
Event::dispatch(new OrderShipped($order)); Event::listen(OrderShipped::class, SendNotification::class); Event::subscribe(UserEventSubscriber::class);
|
Queue 门面
1 2 3 4 5 6 7 8
| use Illuminate\Support\Facades\Queue;
Queue::push(new ProcessPodcast($podcast)); Queue::later(60, new SendEmail($user)); Queue::bulk([ new ProcessFile(1), new ProcessFile(2), ]);
|
Validator 门面
1 2 3 4 5 6 7 8 9 10
| use Illuminate\Support\Facades\Validator;
$validator = Validator::make($data, [ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:users', ]);
if ($validator->fails()) { return $validator->errors(); }
|
View 门面
1 2 3 4 5 6
| use Illuminate\Support\Facades\View;
View::make('welcome', ['name' => 'John']); View::exists('emails.welcome'); View::share('key', 'value'); View::composer('profile', ProfileComposer::class);
|
门面 vs 依赖注入
使用门面
1 2 3 4 5 6 7 8 9 10 11 12 13
| use Illuminate\Support\Facades\Cache;
class UserController extends Controller { public function index() { $users = Cache::remember('users', 3600, function () { return User::all(); });
return response()->json($users); } }
|
使用依赖注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| use Illuminate\Contracts\Cache\Repository as Cache;
class UserController extends Controller { public function __construct( protected Cache $cache ) {}
public function index() { $users = $this->cache->remember('users', 3600, function () { return User::all(); });
return response()->json($users); } }
|
测试门面
模拟门面
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
| use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\Mail;
public function test_cache_facade(): void { Cache::shouldReceive('get') ->once() ->with('key') ->andReturn('value');
$result = Cache::get('key'); $this->assertEquals('value', $result); }
public function test_queue_facade(): void { Queue::fake();
ProcessPodcast::dispatch($podcast);
Queue::assertPushed(ProcessPodcast::class); Queue::assertPushedOn('podcasts', ProcessPodcast::class); }
public function test_mail_facade(): void { Mail::fake();
Mail::to($user)->send(new WelcomeEmail());
Mail::assertSent(WelcomeEmail::class); Mail::assertSent(WelcomeEmail::class, function ($mail) use ($user) { return $mail->hasTo($user->email); }); }
|
门面间谍
1 2 3 4 5 6 7 8 9 10 11
| use Illuminate\Support\Facades\Event;
public function test_event_spy(): void { Event::spy();
event(new OrderShipped($order));
Event::shouldHaveDispatched(OrderShipped::class); Event::shouldNotHaveDispatched(OrderFailed::class); }
|
门面辅助函数
全局辅助函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| cache('key'); cache(['key' => 'value'], 3600);
event(new OrderShipped($order));
info('Message', ['context' => 'data']);
response()->json(['message' => 'Success']);
redirect()->route('home');
view('welcome', ['name' => 'John']);
|
门面最佳实践
1. 选择合适的场景
1 2 3 4 5 6 7 8 9 10 11
| Cache::get('key'); Log::info('Message');
class PaymentController { public function __construct( protected PaymentService $payment ) {} }
|
2. 测试友好
1 2 3 4 5
| Cache::shouldReceive('get')->andReturn('value');
$cache = new CacheManager();
|
3. 保持一致性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class OrderController { public function index() { return Cache::remember('orders', 3600, fn () => Order::all()); } }
class OrderController { public function __construct(protected Cache $cache) {}
public function index() { return $this->cache->remember('orders', 3600, fn () => Order::all()); } }
|
门面列表
| 门面 | 服务容器绑定 |
|---|
| App | app |
| Auth | auth |
| Cache | cache |
| Config | config |
| DB | db |
| Event | events |
| File | files |
| Gate | Gate |
| Log | log |
| Mail | mail |
| Queue | queue |
| Route | router |
| Storage | filesystem |
| View | view |
总结
Laravel 门面提供了一种优雅的方式来访问服务容器中的服务。通过门面,可以使用简洁的静态语法调用方法,同时保持代码的可测试性。理解门面的工作原理和最佳实践,可以帮助开发者编写更优雅、更易维护的 Laravel 代码。记住在简单场景使用门面,在需要依赖注入的场景使用构造函数注入,并充分利用门面的模拟功能进行测试。