使用 Dingo API 快速构建 RESTful API(五)—— 转化器篇(下):结合响应构建器构建 JSON 响应

Dingo API 提供的转化器(Transformer)正是基于 Fractal 对响应数据格式进行一致性的转化。

在 Dingo 中定义的转化器类和 Fractal 中完全一致,Dingo 仅仅是在响应实现时对 Fractal 做了一层封装而已,所以我们可以完全基于上篇教程创建的转化器类进行本篇教程的演示。

为模型类注册转化器

在 Dingo API 中使用转化器最简单的方式就是先注册模型类于转化器之间的映射关系,然后在定义 API 的路由中返回这个模型类实例,Dingo 底层会自动按照映射的转化器对模型对象数据进行转化并返回给客户端。

通常我们会在某个服务提供者(如 AppServiceProvider) 的 boot 方法中注册模型类与转化器之间的映射:

<?php

namespace App\Providers;

use App\Task;
use App\Transformers\TaskTransformer;
use Dingo\Api\Transformer\Factory;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    ...

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        app(Factory::class)->register(Task::class, TaskTransformer::class);
    }
}

然后我们去修改之前创建的 Api\TaskControllershow 方法如下:

public function show($id)
{
    $task = Task::findOrFail($id);
    return $this->response->array($task);
}

再在 Postman 中访问这个路由,就可以获取到通过 TaskTransformer 转化的 Task 对象数据:

Dingo 转化器

这种使用方式在直接返回模型实例的简单 API 中很有用,但是如果涉及到很多模型,或者返回的响应数据很复杂,就不太合适了,所以我们更多时候是将转化器作为参数传入响应构建器,通过二者的结合来构建 JSON 响应,通过这种方式,Dingo 底层会自动注册模型类与转化器的映射。

在响应构建器中使用转化器

由于 Dingo 转化器是基于 Fractal 实现的,所以相关的功能实现也很类似,无非是在 Fractal 的基础上做了一些封装而已,下面我们就来看看如何在 Dingo 响应构建器中使用转化器构建复杂一点的 JSON 响应。

单个资源响应

如果响应返回的是单个模型实例的话,可以借助 Fractal 的单个资源转化来实现,在 Dingo 响应构建器中,对应的是 item 方法,第一个参数传入模型实例,第二个参数传入对应的转化器实例来构建响应,正好我们可以通过修改 Api\TaskControllershow 方法来演示这一场景:

public function show($id)
{
    $task = Task::findOrFail($id);
    return $this->response->item($task, new TaskTransformer());
}

由于 item 方法内部会自动注册模型类与转化器之间的映射,所以可以移除之间在 AppServiceProviderboot 方法中显式注册的映射:

app(Factory::class)->register(Task::class, TaskTransformer::class);

在 Postman 中访问该路由,返回结果和之前一致。

引入关联模型

有的时候,我们还需要引入与指定模型实例相关联的关联模型,基于 Fractal 引入关联模型我们在上篇教程已经介绍过,并且编写好了对应的代码,Dingo API 只是对此做了一层封装,但是对开发者来说,却更加方便,只需要在调用 API 接口时通过 include 请求参数指定要返回的关联模型名称即可,但是这个名称需要和对应转化器中的 $availableIncludes 指定名称保持一致,比如这里我们要获取与指定任务相关联的用户信息,可以这样访问 API 接口:

Dingo 转化器 关联模型

可以看到,我们在请求 URL 中通过设置 include 参数为 user 指定要获取的关联对象,不需要编写任何代码即可在响应中返回对应的关联数据,非常方便。

资源集合响应

如果返回的是多个模型实例,对应的,可以借助 Fractal 的资源集合转化来实现,在 Dingo 响应构建器中,对应的是 collection 方法,第一个参数传入模型实例集合,第二个参数传入转化器实例,我们可以通过编写 Api\TaskControllerindex 方法来演示这一场景:

public function index()
{
    $tasks = Task::all();
    return $this->response->collection($tasks, new TaskTransformer());
}

在 Postman 中,可以通过访问 tasks.index 路由对应 URL 来获取对应的响应数据:

Dingo 转化器 资源集合

分页响应

通常,如果数据量很大的话,在获取任务列表数据的时候,有分页需求,Fractal 提供了对分页数据的支持,在 Dingo 响应构建器中,对应的是 paginator 方法,传入参数和 collection 方法一样,我们通过分页来改写上一步实现的 index 方法如下:

public function index()
{
    $tasks = Task::paginate();
    return $this->response->paginator($tasks, new TaskTransformer());
}

再次访问任务列表 URL,就可以获取到分页信息了:

Dingo 转化器 分页响应

以上,就是我们日常比较常见的 API 接口返回数据格式场景了,此外,在构建 API 响应时,可能还会有其它的一些额外需求,比如设置响应头、响应状态码、元数据等,下面我们来看下这些场景下构建响应。

添加额外的响应头

和 Laravel 框架提供的响应类一样,由于 Dingo 响应构建器提供的是流式接口,所以我们可以通过方法链的方式追加额外的信息到返回的响应实例,比如添加额外的响应头可以这么做:

return $this->response->item($task, new TaskTransformer)->withHeader('Foo', 'Bar');

还可以通过 withHeaders 方法一次添加多个响应头:

return $this->response->item($task, new TaskTransformer())->withHeaders([
    'Foo' => 'Bar',
    'Hello' => 'World'
]);
$cookie = new \Symfony\Component\HttpFoundation\Cookie('foo', 'bar');
return $this->response->item($task, new TaskTransformer())->withCookie($cookie);

设置响应状态码

设置响应状态码可以这么做:

return $this->response->item($task, new TaskTransformer)->setStatusCode(200);

添加元数据

某些转化层可能会使用元数据(meta data),这在你需要提供额外与资源关联的数据时很有用:

return $this->response->item($task, new TaskTransformer)->addMeta('foo', 'bar');

对应返回的数据格式如下:

Dingo 转化器 元数据

还可以设置元数据数组替代多个方法链的调用:

$meta = [
    'foo' => 'bar'
];
return $this->response->item($task, new TaskTransformer())->setMeta($meta);

上一篇: 使用 Dingo API 快速构建 RESTful API(四)—— 转化器篇(上):Fractal 简介及其使用入门

下一篇: 使用 Dingo API 快速构建 RESTful API(六)—— 转化器及响应构建器的高级使用