面向 фаса的探索

发布日期 作者

Reaching for Facades image

Facades,人们似乎要么喜欢要么讨厌。无论如何,它们都是今天 Laravel 的一个自然组成部分。然而,Laravel Facades 并不严格意义上的Facades;是吗?相反,它们是用于从容器中解析类的静态访问器。

当我第一次开始使用 Laravel 时,我讨厌它,在使用 Larvel 三年后,我终于开始接受它。我是你今天典型的 Laravel 讨厌者。当我知道拥抱 Laravel 的本来面目,而不是试图用我的思维方式来对抗框架时,我才学会了爱它。有些人可能会说我现在是它最大的拥护者之一。

有一段时间,我讨厌的一件事就是 Facades。我在抱怨静态方法调用,等等。但我不知道它们在 Laravel 中是如何工作的。我只是加入了其他开发者的噪音,重复他们说过的话,却不知道自己在说什么。

快进到今天,我理解了 Facades 的工作原理——你知道吗?我确实改变了我的看法。我想写这个教程,不是为了让你都同意我的观点,虽然你应该同意,而是为了让你也理解 facades 的工作原理以及它们的优势所在。

这不是严格意义上的教程,因为我会带你浏览我已经写过的现有代码,而我通常会在写教程的时候写代码,这样我就可以解释自然重构点。我将带你浏览的代码是 Get Send Stack Laravel 包,你可以在 GitHub 上找到它.

在构建这个包时,我做了我通常做的事情,开始使用 HTTP Facade 构建一个 API 集成——使用接口/契约来利用 DI 容器在需要时注入实例。让我带你浏览这些代码阶段。我们先从不使用 DI 容器开始。

class AddSubscriberController
{
public function __invoke(AddSubscriberRequest $request)
{
$client = new Client(
url: strval(config('services.sendstack.url')),
token: strval(config('services.sendstack.token')),
);
 
try {
$subscriber = $client->subscribers()->create(
request: new SubscriberRequest(
email: $request->get('email'),
firstName: $request->get('first_name'),
lastName: $request->get('last_name'),
optIn: $request->get('opt_in'),
),
);
} catch (Throwable $exception) {
throw new FailedToSubscribeException(
message: $exception->getMessage(),
previous: $exception,
);
}
 
// return redirect or response.
}
}

所以这有点冗长,但很清楚地看到了你在做什么。你创建客户端,通过客户端发送请求,并捕获可能的异常。最后,根据是 API 还是 Web 控制器,返回重定向或响应。这段代码并不差。你可以测试它。你可以在控制器中轻松地确保行为。

但是,如果该包改变了与它的集成方式,你必须遍历你的代码库,并根据需要进行所有更改,因为你在任何地方都使用新的客户端来处理 API。这是一个考虑重构的最佳时机,因为你通过更聪明地工作来节省未来的工作。直接使用 DI 容器,让我们来看看上面代码的重构版本。

class AddSubscriberController
{
public function __construct(
private readonly ClientContract $client,
) {}
 
public function __invoke(AddSubscriberRequest $request)
{
try {
$subscriber = $this->client->subscribers()->create(
request: new SubscriberRequest(
email: $request->get('email'),
firstName: $request->get('first_name'),
lastName: $request->get('last_name'),
optIn: $request->get('opt_in'),
),
);
} catch (Throwable $exception) {
throw new FailedToSubscribeException(
message: $exception->getMessage(),
previous: $exception,
);
}
 
// return redirect or response.
}
}

现在更简洁、更易于管理,我们从 DI 容器中注入契约/接口,它将为我们解析客户端——因为包服务提供者已经详细说明了如何构建客户端。这种方法没有什么问题;它是我在代码中大量使用的模式。我可以替换实现以获得不同的结果,并且仍然使用相同的包 API,因为我使用的是接口/契约。但同样,当我使用容器时——我是否在对抗框架?我们中许多人喜欢 Laravel 的原因之一是它的开发体验,我们应该感谢 Eloquent。我们不必费心与容器进行杂耍来创建一个新模型或类似的东西。我们非常习惯在需要的时候静态调用我们想要的东西。所以让我们看看上面使用我在包中创建的 Facade 的示例。

class AddSubscriberController
{
public function __invoke(AddSubscriberRequest $request)
{
try {
$subscriber = SendStack::subscribers()->create(
request: new SubscriberRequest(
email: $request->get('email'),
firstName: $request->get('first_name'),
lastName: $request->get('last_name'),
optIn: $request->get('opt_in'),
),
);
} catch (Throwable $exception) {
throw new FailedToSubscribeException(
message: $exception->getMessage(),
previous: $exception,
);
}
 
// return redirect or response.
}
}

不再需要担心容器——我们又回到了我们所缺少的熟悉的 Laravel 感觉。这里的优点是开发体验简单直观,实现看起来很干净,并且我们获得了相同的结果。缺点是什么?当然,有一些缺点。唯一的缺点是,你不能切换实现,因为 Facade 对它的实现是静态的。但根据我的经验,当你谈论外部服务时,从提供商 A 迁移到提供商 B 比创建和绑定一个新的实现到容器要复杂得多。那些总是敲打这面鼓的人用狭隘的意识形态范围来看待这个问题。实际上,更改提供商是一项巨大的工作,不仅仅是从代码的角度来看——所以总有足够的时间专注于在需要的地方实现不同的东西。有时,新提供商有一些老提供商没有的东西。也许你必须在你的请求中发送额外的數據等等。

我的观点是,虽然 SOLID 原则是很棒的,你应该参考它们来获得建议——但它们通常是不切实际的梦想,在实践中行不通,或者你将花费太多时间编写功能,以至于范围在你完成之前就发生了变化。在每个转折点对抗框架并不能帮助你构建好的产品。你通过接受不完美并承认可能需要改变来创建好的产品。

这与 Facades 有什么关系?正如你从代码示例中看到的,Facades 在许多方面使事情变得更容易。这两种方式都没有错,也没有哪种方式是对的。Facade 将允许更友好的实现,但会迫使你走上一条特定的道路。使用容器将允许你未来有更大的灵活性,但它不是万能药,也存在自身的风险。当你需要的时候简单地新建实例很容易,但当有更好的方法来实现相同的结果时,这样做就很懒惰了。

Facade 到底长什么样?这是包中的确切代码。

declare(strict_types=1);
 
namespace SendStack\Laravel\Facades;
 
use Illuminate\Support\Facades\Facade;
use SendStack\Laravel\Contracts\ClientContract;
use SendStack\Laravel\Http\Resources\SubscribersResource;
use SendStack\Laravel\Http\Resources\TagResource;
 
/**
* @method static SubscribersResource subscribers()
* @method static TagResource tags()
* @method static bool isActiveSubscriber(string $email)
*
* @see ClientContract
*/
class SendStack extends Facade
{
protected static function getFacadeAccessor()
{
return ClientContract::class;
}
}

它有一个受保护的静态方法,用于获取它需要构造和构建的类,并且我们扩展的类将把所有静态调用转发到这个类,一旦从容器中解析出来。人们把 Facades 说成是脏话,但实际上,它与创建容器别名没什么区别,只是语法不同。在我的例子中,我在实现/接口上的方法中添加了文档块,以便更好地进行 IDE 完成——但这只是一个我喜欢采取的额外步骤。

这个故事的寓意是,Facades 并不邪恶,实际上可能非常有用——所以忽略那些讨厌它的人,像我一样拥抱它。你会为此感到高兴,并且会更加高效。

Steve McDougall photo

技术作家 Laravel 新闻,开发人员倡导者 Treblle。API 专家,资深 PHP/Laravel 工程师。 YouTube 直播主.

Cube

Laravel 新闻通讯

加入 40,000 多位其他开发人员,绝不错过新的技巧、教程等等。

Laravel Forge logo

Laravel Forge

轻松创建和管理您的服务器,并在几秒钟内部署您的 Laravel 应用程序。

Laravel Forge
Tinkerwell logo

Tinkerwell

Laravel 开发人员必备的代码运行器。使用 AI、自动完成功能和即时反馈来调试本地和生产环境。

Tinkerwell
No Compromises logo

绝不妥协

Joel 和 Aaron 是来自 No Compromises 播客的两位经验丰富的开发者,现在可以为您的 Laravel 项目提供服务。 ⬧ 固定价格 $7500/月。 ⬧ 无冗长的销售流程。 ⬧ 无需签订合同。 ⬧ 100% 退款保证。

绝不妥协
Kirschbaum logo

Kirschbaum

提供创新和稳定性,确保您的 Web 应用程序取得成功。

Kirschbaum
Shift logo

Shift

正在使用旧版本的 Laravel?立即进行自动化 Laravel 升级和代码现代化,让您的应用程序保持新鲜。

Shift
Bacancy logo

Bacancy

只需每月 $2500,即可让经验丰富的 Laravel 开发人员 (4-6 年经验) 为您的项目增光添彩。获得 160 小时的专业技能和 15 天的无风险试用。立即预约电话咨询!

Bacancy
Lucky Media logo

Lucky Media

立即获得 Lucky - Laravel 开发的理想选择,拥有十多年的经验!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel 电子商务

Laravel 电子商务。一个开源包,将现代无头电子商务功能的力量带到 Laravel。

Lunar: Laravel 电子商务
LaraJobs logo

LaraJobs

官方 Laravel 招聘网站

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS 启动套件

SaaSykit 是一个 Laravel SaaS 启动套件,包含运行现代 SaaS 所需的所有功能。付款、精美结账、管理面板、用户仪表盘、身份验证、就绪组件、统计信息、博客、文档等等。

SaaSykit: Laravel SaaS 启动套件
Rector logo

Rector

您的无缝 Laravel 升级合作伙伴,降低成本,加速创新,帮助企业取得成功

Rector
MongoDB logo

MongoDB

通过 MongoDB 和 Laravel 的强大集成来增强您的 PHP 应用程序,使开发人员能够轻松高效地构建应用程序。支持事务性、搜索、分析和移动用例,同时使用熟悉的 Eloquent API。了解 MongoDB 的灵活、现代数据库如何改变您的 Laravel 应用程序。

MongoDB

最新消息

查看全部 →
Maska is a Simple Zero-dependency Input Mask Library image

Maska 是一个简单的零依赖输入掩码库

阅读文章
Add Swagger UI to Your Laravel Application image

将 Swagger UI 添加到您的 Laravel 应用程序

阅读文章
Assert the Exact JSON Structure of a Response in Laravel 11.19 image

在 Laravel 11.19 中断言响应的精确 JSON 结构

阅读文章
Build SSH Apps with PHP and Laravel Prompts image

使用 PHP 和 Laravel 提示构建 SSH 应用程序

阅读文章
Building fast, fuzzy site search with Laravel and Typesense image

使用 Laravel 和 Typesense 构建快速、模糊的网站搜索

阅读文章
Add Comments to your Laravel Application with the Commenter Package image

使用 Commenter 包在您的 Laravel 应用程序中添加评论

阅读文章