学习如何在 Laravel 中创建自定义 Facade
Laravel Facade 是 Laravel 框架的一个出色特性,通过简单的接口提供对服务的便捷访问。初学 Laravel时,有一件事让我感到困惑,那就是 Facade 访问器方法。
一旦你理解了 Facade 是访问 Laravel 服务容器中底层服务的便捷方式,你就可以轻松地跟踪 Facade 背后的服务。每个 Facade 都提供一个指向已注册服务名称的 getFacadeAccessor()
方法。
以下是 DB
Facade 的 Facade 访问器示例:
// Illuminate\Support\Facades\DB;
protected static function getFacadeAccessor()
{
return 'db';
}
db
字符串指向了容器中的一个服务,该 Facade 会调用此服务。如果你跳到 tinker 会话,你可以自己尝试一下:
有些 Facade 可能会解析到 Manager 类,然后会动态将方法传递给底层的 macro 或数据库连接列:
public function __call($method, $parameters)
{
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}
return $this->connection()->$method(...$parameters);
}
当在没有连接的情况下调用时,DatabaseManager
将使用默认连接(上例中为 sqlite),并调用该连接上的方法。
我建议阅读 Laravel 文档中的了解 Facade 如何工作。
在 Laravel 中创建自己的 Facade
如果你没有创建插件或直接使用 Laravel 框架,为什么要在应用中创建 Facade?一些开发人员更喜欢只对应用代码中定义的服务使用依赖注入,这很好。但是,我发现通过 helper 或 App\Facades
命名空间为我经常使用的服务定义 Facades 很方便。我喜欢 Laravel 的灵活性,同时仍然制定了在不熟悉的代码库中提高效率的惯例。
要在应用中创建 Facade,我建议使用 make:class
命令来生成 Facade,你可以按如下方式操作:
php artisan make:class App/Facades/Example
假设你在应用的服务提供者中定义了一个名为 App\ExampleService
的服务,你可以在生成该类后,为其创建一个 Facade;
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Example extends Facade
{
public static function getFacadeAccessor()
{
return 'example_service';
}
}
如果你没有在服务提供者中定义别名或使用字符串来定义它,你也可以很容易地将 Facade 访问器设置为完全限定类的字符串:
public static function getFacadeAccessor()
{
return \App\ExampleService::class;
}
Facades 对于在测试中直接模拟底层服务类提供了便利:
use App\Facades\Example;
Example::shouldReceive('getLatestPosts')
->with($after_date)
->andReturn($test_posts);
如果你并未使用 Facade,Laravel 提供了其他方便的模拟方法,比如 partialMock()
,可以在测试中方便地将服务交换为模拟。使用该服务,你可以直接在测试中这样做:
$mock = $this->partialMock(MyApiService::class, function (MockInterface $mock) {
$mock->shouldReceive('getLatestPosts')
->with($after_date)
->andReturn($test_posts);
});
采取哪种方法取决于你,但如果在应用中大量使用某个服务,请考虑创建一个 Facade,以便在使用服务时提供一些便利,而不会丢失依赖注入提供的功能。