Laravel Response Classes 响应类使用优化浅析

2023-06-01 00:00:00 优化 响应 浅析

在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 Enum将返回类似的东西,它唯一的工作就是返回状态代码。

我更喜欢这种更轻量级的实用方法,因为你清楚地知道正在发生什么,以及这个类或包可能做什么。

问题是,有时你使用的类做的事情太多,以至于你不得不把这个巨大的东西加载到内存中,只是为了能够返回一个整数值。

这没有什么意义,所以我建议使用一个专门的包或类来自己管理这些。

比如:

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响应类给我们带来的好处--但允许我们有更多的背景和可预测性。


然而,我们现在面临着其他领域的代码重复问题。

在我们的响应类本身。其中许多看起来很相似,唯一的区别是构造器的属性将是不同的类型。

我们想保持使用自定义响应类的上下文,但我们想避免为一个属性创建一个庞大的联合型参数的东西--这时我们不妨添加混合型的,就可以了。


在这种情况下,你可以伸手去找一个抽象类来扩展,或者找一个特质来把行为添加到需要它的类中。

就我个人而言,我是一个喜欢组合而不是继承的人,所以使用trait对我来说更有意义。

trait SendsResponse
{
    public function toResponse($request): Response
    {
        return new JsonResponse(
            data: $this->data,
            status: $this->status->value,
        );
    }
}

这种方法最大的问题是静态分析会抱怨这段代码,因为trait需要拥有或了解类的属性。

可以这样解决:

/**
 * @property-read mixed $data
 * @property-read Http $status
 */

我们可以把这个文档块添加到trait中,这样它就能知道它所能访问的属性。

现在我们的Response类在使用和构建上会简单很多,代码中的重复部分也会减少。

class MessageResponse implements Responsable
{
    use SendsResponse;
 
    public function __construct(
        public readonly array $data,
        public readonly Http $status = Http::OK,
    ) {}
}

现在我们可以很容易地建立起我们需要发送的所有潜在响应,保持类型安全,减少代码重复。

相关文章