Facades

Introduction

在整个 Laravel 文档中,您将看到通过“门面”与 Laravel 功能交互的代码示例。 Facades 为应用程序中可用的类提供了一个“静态”接口服务容器. Laravel 附带了许多外观,可以访问几乎所有 Laravel 的功能。

Laravel facades 充当服务容器中底层类的“静态代理”,提供简洁、表​​达语法的好处,同时保持比传统静态方法更多的可测试性和灵活性。如果你不完全理解 Facades 是如何工作的,那也没关系——只要顺其自然,继续学习 Laravel。

Laravel 的所有外观都定义在Illuminate\Support\Facades 命名空间。因此,我们可以像这样轻松访问外观:

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;

Route::get('/cache', function () {
    return Cache::get('key');
});

在整个 Laravel 文档中,许多示例将使用外观来演示框架的各种功能。

辅助功能

为了补充门面,Laravel 提供了多种全局“辅助函数”,可以更轻松地与常见的 Laravel 功能进行交互。您可能会与之交互的一些常见帮助函数是view,response,url,config, 和更多。 Laravel 提供的每个辅助函数都有相应的功能文档;但是,完整列表可在专门的帮助文件.

例如,而不是使用Illuminate\Support\Facades\Response facade 来生成 JSON 响应,我们可以简单地使用response 功能。因为辅助函数是全局可用的,所以你不需要导入任何类来使用它们:

use Illuminate\Support\Facades\Response;

Route::get('/users', function () {
    return Response::json([
        // ...
    ]);
});

Route::get('/users', function () {
    return response()->json([
        // ...
    ]);
});

何时使用立面

立面有很多好处。它们提供了一种简洁、令人难忘的语法,使您可以使用 Laravel 的功能,而无需记住必须手动注入或配置的长类名。此外,由于它们独特地使用了 PHP 的动态方法,因此它们易于测试。

但是,在使用外墙时必须小心。立面的主要危险是类“范围蔓延”。由于 Facades 非常易于使用且不需要注入,因此很容易让您的类继续增长并在单个类中使用许多 Facades。使用依赖注入,大型构造函数给你的视觉反馈减轻了这种潜力,即你的类变得太大了。因此,在使用外观时,要特别注意类的大小,以使其职责范围保持狭窄。如果您的班级太大,请考虑将其分成多个较小的班级。

外墙比。依赖注入

依赖注入的主要好处之一是能够交换注入类的实现。这在测试期间很有用,因为您可以注入模拟或存根并断言在存根上调用了各种方法。

通常,不可能模拟或存根一个真正的静态类方法。然而,由于外观使用动态方法来代理对从服务容器解析的对象的方法调用,我们实际上可以像测试注入的类实例一样测试外观。例如,给定以下路线:

use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
    return Cache::get('key');
});

使用 Laravel 的门面测试方法,我们可以编写如下测试来验证Cache::get 使用我们期望的参数调用方法:

use Illuminate\Support\Facades\Cache;

/**
 * A basic functional test example.
 */
public function test_basic_example(): void
{
    Cache::shouldReceive('get')
         ->with('key')
         ->andReturn('value');

    $response = $this->get('/cache');

    $response->assertSee('value');
}

外墙比。辅助功能

除了门面之外,Laravel 还包含各种“辅助”功能,这些功能可以执行常见任务,例如生成视图、触发事件、调度作业或发送 HTTP 响应。许多这些辅助函数执行与相应外观相同的功能。例如,这个 facade 调用和 helper 调用是等价的:

return Illuminate\Support\Facades\View::make('profile');

return view('profile');

门面和辅助函数之间绝对没有实际区别。使用辅助函数时,您仍然可以像测试相应的外观一样完全测试它们。例如,给定以下路线:

Route::get('/cache', function () {
    return cache('key');
});

cache 助手将调用get 底层类的方法Cache 正面。因此,即使我们使用的是辅助函数,我们也可以编写以下测试来验证是否使用我们预期的参数调用了该方法:

use Illuminate\Support\Facades\Cache;

/**
 * A basic functional test example.
 */
public function test_basic_example(): void
{
    Cache::shouldReceive('get')
         ->with('key')
         ->andReturn('value');

    $response = $this->get('/cache');

    $response->assertSee('value');
}

立面如何工作

在 Laravel 应用程序中,外观是一个类,它提供对容器中对象的访问。使这项工作的机器是在Facade 班级。 Laravel 的外观,以及您创建的任何自定义外观,都将扩展基础Illuminate\Support\Facades\Facade 班级。

Facade 基类利用了__callStatic() 魔术方法将调用从您的外观延迟到从容器解析的对象。在下面的示例中,调用了 Laravel 缓存系统。看一眼这段代码,人们可能会假设静态get 正在调用方法Cache 班级:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;

class UserController extends Controller
{
    /**
     * Show the profile for the given user.
     */
    public function showProfile(string $id): View
    {
        $user = Cache::get('user:'.$id);

        return view('profile', ['user' => $user]);
    }
}

请注意,在文件顶部附近,我们正在“导入”Cache 正面。这个外观作为访问底层实现的代理Illuminate\Contracts\Cache\Factory 界面。我们使用外观进行的任何调用都将传递给 Laravel 缓存服务的底层实例。

如果我们看那个Illuminate\Support\Facades\Cache 类,你会看到没有静态方法get:

class Cache extends Facade
{
    /**
     * Get the registered name of the component.
     */
    protected static function getFacadeAccessor(): string
    {
        return 'cache';
    }
}

相反,Cache 立面延伸了基地Facade 类并定义方法getFacadeAccessor().此方法的工作是返回服务容器绑定的名称。当用户引用任何静态方法时Cache facade,Laravel 解决了cache 绑定来自服务容器 并运行请求的方法(在这种情况下,get) 针对该对象。

实时立面

使用实时外观,您可以将应用程序中的任何类视为外观。为了说明如何使用它,让我们首先检查一些不使用实时外观的代码。例如,假设我们的Podcast 模型有一个publish 方法。然而,为了发布播客,我们需要注入一个Publisher 实例:

<?php

namespace App\Models;

use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;

class Podcast extends Model
{
    /**
     * Publish the podcast.
     */
    public function publish(Publisher $publisher): void
    {
        $this->update(['publishing' => now()]);

        $publisher->publish($this);
    }
}

将发布者实现注入方法使我们能够轻松地隔离测试方法,因为我们可以模拟注入的发布者。但是,它要求我们每次调用publish 方法。使用实时外观,我们可以保持相同的可测试性,同时不需要显式地通过Publisher 实例。要生成实时门面,请在导入类的命名空间前添加前缀Facades:

<?php

namespace App\Models;

use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;

class Podcast extends Model
{
    /**
     * Publish the podcast.
     */
    public function publish(): void
    {
        $this->update(['publishing' => now()]);

        Publisher::publish($this);
    }
}

当使用实时门面时,发布者实现将使用出现在Facades 字首。在测试时,我们可以使用 Laravel 内置的门面测试助手来模拟这个方法调用:

<?php

namespace Tests\Feature;

use App\Models\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class PodcastTest extends TestCase
{
    use RefreshDatabase;

    /**
     * A test example.
     */
    public function test_podcast_can_be_published(): void
    {
        $podcast = Podcast::factory()->create();

        Publisher::shouldReceive('publish')->once()->with($podcast);

        $podcast->publish();
    }
}

门面类参考

您将在下面找到每个外观及其底层类。这是一个有用的工具,可用于快速挖掘给定外观根的 API 文档。这服务容器绑定 密钥也包括在适用的地方。

Facade Class 服务容器绑定
App Illuminate\Foundation\Application app
Artisan Illuminate\Contracts\Console\Kernel artisan
Auth Illuminate\Auth\AuthManager auth
授权(实例) Illuminate\Contracts\Auth\Guard auth.driver
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Broadcast Illuminate\Contracts\Broadcasting\Factory  
广播(实例) Illuminate\Contracts\Broadcasting\Broadcaster  
Bus Illuminate\Contracts\Bus\Dispatcher  
Cache Illuminate\Cache\CacheManager cache
缓存(实例) Illuminate\Cache\Repository cache.store
Config Illuminate\Config\Repository config
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
Date Illuminate\Support\DateFactory date
DB Illuminate\Database\DatabaseManager db
数据库(实例) Illuminate\Database\Connection db.connection
Event Illuminate\Events\Dispatcher events
File Illuminate\Filesystem\Filesystem files
Gate Illuminate\Contracts\Auth\Access\Gate  
Hash Illuminate\Contracts\Hashing\Hasher hash
Http Illuminate\Http\Client\Factory  
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\LogManager log
Mail Illuminate\Mail\Mailer mailer
Notification Illuminate\Notifications\ChannelManager  
Password Illuminate\Auth\Passwords\PasswordBrokerManager auth.password
密码(实例) Illuminate\Auth\Passwords\PasswordBroker auth.password.broker
Queue Illuminate\Queue\QueueManager queue
队列(实例) Illuminate\Contracts\Queue\Queue queue.connection
队列(基类) Illuminate\Queue\Queue  
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\RedisManager redis
Redis(实例) Illuminate\Redis\Connections\Connection redis.connection
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory  
响应(实例) Illuminate\Http\Response  
Route Illuminate\Routing\Router router
Schema Illuminate\Database\Schema\Builder  
Session Illuminate\Session\SessionManager session
会话(实例) Illuminate\Session\Store session.store
Storage Illuminate\Filesystem\FilesystemManager filesystem
存储(实例) Illuminate\Contracts\Filesystem\Filesystem filesystem.disk
URL Illuminate\Routing\UrlGenerator url
Validator Illuminate\Validation\Factory validator
验证器(实例) Illuminate\Validation\Validator  
View Illuminate\View\Factory view
查看(实例) Illuminate\View\View  
Vite Illuminate\Foundation\Vite  
豫ICP备18041297号-2