Laravel 响应类
发布时间: 作者: 史蒂夫·麦克杜格尔
从你的 Laravel 应用程序响应,我认为是至关重要的,尤其是当你构建一个 API 时。让我们看看如何增强我们的响应。
我们中的许多人通常会从在应用程序中使用辅助函数开始,因为文档和许多教程都会使用它们。它们很容易上手,并且完全按照你的预期执行。让我们看看这些代码是什么样子的
return response()->json( data: [], status: 200,);
这是一个略微夸张的例子,你通常会传递数据并跳过状态码。然而,对我来说,习惯很难改变!
此代码将为你创建一个新的 JsonResponse
,并将数据和状态码传入,以便你返回它。这有效,并且使用这种方法没有任何问题。如果你已经使用它了,提高你的 API 游戏的一种方法是在这里添加状态码,以便对你返回的内容更具声明性。
展望未来,我们可以跳过使用辅助函数,并开始使用辅助函数创建的底层类。
return new JsonResponse( data: [], status: 200,);
我喜欢这种方法,因为它对辅助函数的依赖较少,并且更具声明性。查看代码,你确切地知道返回了什么,因为它就在你眼前,而不是隐藏在辅助函数后面。你可以通过使用常量或另一种方法来声明状态码本身来提升它,从而使它更易于阅读和理解,即使是那些可能不了解所有状态码的开发人员。让我们看看它可能是什么样子的
return new JsonResponse( data: [], status: JsonResponse::HTTP_OK,);
JsonResponse
类通过几个抽象层扩展了 Symfony Response 类,因此你可以直接调用它,但是,你的静态分析器可能会对此进行抱怨。我创建了一个名为 juststeveking/http-status-code
的包,这是一个 PHP 枚举,它将返回类似的东西,它唯一的任务是返回状态码。我更喜欢这种更轻量级的实用方法来处理像这样的事情,因为你确切地知道发生了什么以及这个类或包可能做什么。问题有时在于,你使用的类做了太多事情,以至于你必须将这个庞大的东西加载到内存中才能返回一个整数值。这没有多大意义,所以我建议使用专用包或类自己管理它。让我们看看这样做时它是什么样子的
return new JsonResponse( data: [], status: Http::OK->value,);
这是我们代码的声明性如何清晰的一个重要进步。它易于阅读和理解正在发生的事情。但是,我们发现自己一次又一次地创建相同的代码块,那么我们如何解决这个问题呢?
答案很简单 - 响应类。在 Laravel 中,有一个我们可以使用的契约叫做 Responsable
,它告诉我们我们的类必须有一个 toResponse
方法。我们可以直接从控制器返回它,因为 Laravel 会解析并理解这些类,没有问题。让我们看看这些类的一个简单的基本示例
class MyJsonResponse implements Responsable{ public function __construct( public readonly array $data, public readonly Http $status = Http::OK, ) {} public function toResponse($request): Response { return new JsonResponse( data: $this->data, status: $this->status->value, ); }}
这是一个简单的使用方式。但是,它没有为我们的应用程序添加任何价值。它只是对现有内容的抽象。让我们看看一些可能为我们的应用程序添加更多价值的内容。
class CollectionResponse implements Responsable{ public function __construct( public readonly JsonResourceCollection $data, public readonly Http $status = Http::OK, ) {} public function toResponse($request): Response { return new JsonResponse( data: $this->data, status: $this->status->value, ); }}
现在我们有一个响应类,它将处理我们传递的任何资源集合,使其在我们应用程序中非常可重用。让我们看看如何在控制器中返回它
return new CollectionResponse( data: UserResource::collection( resource: User::query()->get(), ),);
它更简洁,代码重复更少,并且如果需要,可以轻松覆盖默认状态。它为我们提供了辅助方法和 Json Response 类提供的优势,但允许我们有更多上下文和可预测性。
但是,我们现在面临着在其他区域重复代码的问题。在我们自己的响应类中。许多这些看起来很相似,唯一的区别在于构造函数属性将是不同类型。我们想要保留使用自定义响应类的上下文,但我们想要避免为属性创建具有大量联合类型参数的内容,当我们可以添加 mixed 并完成它时。
在这种情况下,你可以选择使用抽象类进行扩展,或者使用特性将行为添加到需要它的类中。就我个人而言,我更喜欢组合而不是继承,因此使用特性对我来说更有意义。
trait SendsResponse{ public function toResponse($request): Response { return new JsonResponse( data: $this->data, status: $this->status->value, ); }}
这种方法最大的问题是静态分析将对这段代码提出抱怨,因为特性需要拥有或知道类的属性。但这很容易解决。
/** * @property-read mixed $data * @property-read Http $status */
我们可以将此文档块添加到特性中,以便它了解它可以访问的属性。
现在我们的响应类将更容易使用和构建,代码重复更少。
class MessageResponse implements Responsable{ use SendsResponse; public function __construct( public readonly array $data, public readonly Http $status = Http::OK, ) {}}
现在我们可以轻松地构建出我们需要发送的所有潜在响应,保持类型安全并减少代码重复。