热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用DingoAPI扩展包快速构建LaravelRESTfulAPI(四)——转化器篇(上):Fractal及其使用入门

DingoAPI中的转化器(Transformer)有点类似Laravel框架自带的API资源类,都是用于对返回的响应数据进行格式化,通过转化器,你可以轻松实现将对象转化为数组,并支持整型和布尔类型之间的转化,以及分页结果和嵌套关联。这篇教程我们主要讨论转化器在DingoAPI中的使用,这里的转化器包括以下两层意思:在介绍Dingo转化器使用之前,有必要大致了解下其底层实现原理。

转化器简介

Dingo API 中的转化器(Transformer)有点类似 Laravel 框架自带的API 资源类,都是用于对返回的响应数据进行格式化,通过转化器,你可以轻松实现将对象转化为数组,并支持整型和布尔类型之间的转化,以及分页结果和嵌套关联。

这篇教程我们主要讨论转化器在 Dingo API 中的使用,这里的转化器包括以下两层意思:

  • 转化层(transformation layer):准备和处理转化器的库;
  • 转化器(transformer):获取原始数据并将其转化为数组格式的类,转化器的具体处理方式取决于转化层。

在介绍 Dingo 转化器使用之前,有必要大致了解下其底层实现原理。

Fractal 概述

Dingo API 底层使用 Fractal 作为默认的转化层,Fractal 库能够为复杂的数据输出提供表示和转化层,常用于基于 JSON 的 RESTful API,作为一个数据转化层,Fractal 具备以下特点:

  • 在数据源与最终输出数据之间进行隔离,从而避免数据源格式的变化对接口调用方的影响;
  • 提供系统的数据类型转化支持,避免大量的 foreach 和到处进行强制数据类型转化(如 (bool)(int) 等);
  • 支持复杂数据结构的嵌入和嵌套关联;
  • 使用 HAL 和 JSON-API 等标准进行数据转化,但也支持自定义格式;
  • 支持对数据结果进行分页;
  • 可以简化 API 接口输出数据构建的复杂性。

为了更好的理解 Dingo 转化器的创建和使用,我们先简单介绍下 Fractal 的使用。

Fractal 使用入门

使用 Fractal 之前,需要先通过 Composer 安装相应的扩展包:

composer require league/fractal

不过,由于我们先前已经安装过 Dingo 扩展包,而 Dingo 扩展包又依赖了 Fractal 扩展包,所以该扩展包已经随着 Dingo 扩展包的安装而安装了,不需要重复安装。

Fractal 有几个术语需要解释,理解了这些术语之后,就基本掌握了 Fractal,从而为 Dingo 转化器的使用打下基础。

资源

所谓「资源」指的是用于表示数据的对象,资源主要分为两类:

League\Fractal\Resource\Item
League\Fractal\Resource\Collection

ItemCollection 构造器接收任意你想要发送的数据作为第一个参数,以及一个对应的「转化器」作为第二个参数(对应源码位于 League\Fractal\Resource\ResourceAbstract 基类中):

/**
 * Create a new resource instance.
 *
 * @param mixed                             $data
 * @param callable|TransformerAbstract|null $transformer
 * @param string                            $resourceKey
 */
public function __construct($data = null, $transformer = null, $resourceKey = null)
{
    $this->data = $data;
    $this->transformer = $transformer;
    $this->resourceKey = $resourceKey;
}

转化器是一个用于定义输出数据格式的类或回调函数。下面我们以单个资源为例,在 Laravel 中基于 Fractal 定义一个 API 接口:

Route::get('/fractal/resource/item', function () {
    $task = \App\Task::findOrFail(1);
    $resource = new \League\Fractal\Resource\Item($task, function (\App\Task $task) {
        return [
            'id' => $task->id,
            'text' => $task->text,
            'is_completed' => $task->is_completed ? 'yes' : 'no'
        ];
    });
    $fractal = new \League\Fractal\Manager();
    return $fractal->createData($resource)->toJson();
});

这里我们通过传入闭包函数来定义转化器,关于转化器类的实现后面转化器部分会介绍。如果是集合资源的话,处理方式类似:

Route::get('/fractal/resource/collection', function () {
    $tasks = \App\Task::all();
    $resource = new \League\Fractal\Resource\Collection($tasks, function (\App\Task $task) {
        return [
            'id' => $task->id,
            'text' => $task->text,
            'is_completed' => $task->is_completed ? 'yes' : 'no'
        ];
    });
    $fractal = new \League\Fractal\Manager();
    return $fractal->createData($resource)->toJson();
});

序列化器

在 Fractal 中,我们可以通过设置序列化器来指定数据的转化格式,在 API 接口中有很多可以选择的数据输出格式,最著名的就是 HAL 和 JSON-API ,Fractal 默认支持 ArraySerializerDataArraySerializerJsonApiSerializer 三种序列化器,此外,还支持自定义序列化器。不同的序列化器的区别主要体现在数据命名空间的组织上,通过这些序列化器,你可以在 Fractal 中快速实现不同数据输出格式的切换,而不需要对转化器做任何修改。

首先我们来看下 ArraySerializer 的数据输出格式:

Route::get('/fractal/serializers', function () {
    $task = \App\Task::findOrFail(1);
    $resource = new \League\Fractal\Resource\Item($task, function (\App\Task $task) {
        return [
            'id' => $task->id,
            'text' => $task->text,
            'is_completed' => $task->is_completed ? 'yes' : 'no'
        ];
    });
    $fractal = new \League\Fractal\Manager();
    $fractal->setSerializer(new \League\Fractal\Serializer\ArraySerializer());
    return $fractal->createData($resource)->toJson();
});

可以看到,我们通过调用 Fractal 管理器实例上的 setSerializer 方法来设置序列化器,以上代码返回响应数据格式如下:

使用 Dingo API 扩展包快速构建 Laravel RESTful API(四) —— 转化器篇(上):Fractal 及其使用入门

再来看下 DataArraySerializer 的数据输出格式,其它代码不变,将序列号器设置那行代码修改如下:

$fractal->setSerializer(new \League\Fractal\Serializer\DataArraySerializer());

对应返回响应输出格式如下,与 ArraySerializer 相比,多出了一层 data 包裹:

使用 Dingo API 扩展包快速构建 Laravel RESTful API(四) —— 转化器篇(上):Fractal 及其使用入门

需要指出的是, DataArraySerializer 是 Fractal 默认的数据输出格式。

最后,再看下 JsonApiSerializer 的数据输出格式,还是调整序列号器设置那行代码:

$fractal->setSerializer(new \League\Fractal\Serializer\JsonApiSerializer());

返回响应对应数据格式如下,该格式遵循 JSON-API 标准:

使用 Dingo API 扩展包快速构建 Laravel RESTful API(四) —— 转化器篇(上):Fractal 及其使用入门

如果以上都不能满足你的需求,还可以创建一个继承自 SerializerAbstract 基类的子类来自定义返回响应的数据格式。

转化器

在「资源」部分,我们已经提到了「转化器」的概念,只是那里是通过回调函数来实现的,只能一次性使用,现在,我们通过独立的类来实现,以提高代码的可复用性。

转化器类必须继承自 League\Fractal\TransformerAbstract 基类,并且至少实现 transform() 方法。我们在代码任务项目中创建一个保存在 app/Transformers 目录下的转化器类 TaskTransformer ,并初始化代码如下:

 $task->id,
            'text' => $task->text,
            'completed' => $task->is_completed ? 'yes' : 'no',
            'link' => route('tasks.show', ['id' => $task->id])
        ];
    }
}

这样一来,我们就可以改写之前的资源转化代码如下:

// 获取单个资源
$task = \App\Task::findOrFail(1);
$resource = new \League\Fractal\Resource\Item($task, new \App\Transformers\TaskTransformer());

// 获取资源集合
$tasks = \App\Task::all();
$resources = new \League\Fractal\Resource\Collection($tasks, new \App\Transformers\TaskTransformer());

除此之外,我们还可以在模型字段之外,引入额外的数据,比如关联模型:

 $task->id,
            'text' => $task->text,
            'completed' => $task->is_completed ? 'yes' : 'no',
            'link' => route('tasks.show', ['id' => $task->id])
        ];
    }

    public function includeUser(Task $task)
    {
        $user = $task->user;
        return $this->item($user, new UserTransformer());
    }
}

由于在上述代码中引入了新的转化器类 UserTransformer ,所以需要创建它:

 $user->id,
            'name' => $user->name
        ];
    }
}

然后修改返回响应数据代码如下,通过 parseIncludes 方法引入要包含的额外字段:

return $fractal->parseIncludes('user')->createData($resource)->toJson();

这样一来,就可以在返回的响应数据中看到 user 字段了:

使用 Dingo API 扩展包快速构建 Laravel RESTful API(四) —— 转化器篇(上):Fractal 及其使用入门

除此之外,Fractal 还支持引入默认额外字段、排除指定字段、引入 URL 查询参数字段等,更多细节请参考 官方文档 ,这里就不一一列举了。

分页

Fractal 提供了两种解决方案来支持分页数据结果,分别是分页器和游标,下面我们简单演示下如何使用它们。

使用分页器

分页器可以提供丰富的分页结果信息,包括项目总数、上一页/下一页链接等,但相应的代价是可能会带来额外的性能开销,比如每次调用都要统计项目总数,如果对性能要求比较苛刻,可以考虑使用游标来获取分页结果。

当我们使用分页器的时候,创建的分页器类必须实现 League\Fractal\Pagination\PaginatorInterface 接口,然后将实例化后的分页器对象传入 League\Fractal\Resource\Collection::setPaginator() 方法。

为了与当前流行的 PHP 框架兼容,Fractal 提供了以下适配器,方便我们快速在相应的 PHP 框架中集成 Fractal:

League\Fractal\Pagination\IlluminatePaginatorAdapter
League\Fractal\Pagination\PagerfantaPaginatorAdapter
League\Fractal\Pagination\PhalconFrameworkPaginatorAdapter
League\Fractal\Pagination\ZendFrameworkPaginatorAdapter

至于为什么使用分页适配器,是为了将不同框架实现的分页器转化为符合 Fractal 规范的分页器。

当然,我们这里以 Laravel 框架为例,演示在 Laravel 项目中基于 Fractal 使用分页适配器对分页结果进行处理:

Route::get('fractal/paginator', function () {
    $paginator = \App\Task::paginate();
    $tasks = $paginator->getCollection();

    $resource = new \League\Fractal\Resource\Collection($tasks, new \App\Transformers\TaskTransformer());
    $resource->setPaginator(new \League\Fractal\Pagination\IlluminatePaginatorAdapter($paginator));

    $fractal = new \League\Fractal\Manager();
    return $fractal->createData($resource)->toJson();
});

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

使用 Dingo API 扩展包快速构建 Laravel RESTful API(四) —— 转化器篇(上):Fractal 及其使用入门

使用游标

如果数据结果集特别大,运行 select count(*) from sometable 会有很大的性能开销,可以考虑使用游标来分批获取分页结果,游标的使用方式也很简单,和分页器类似,首先需要定义一个实现了 League\Fractal\Pagination\CursorInterface 接口的游标类,实例化之后将对应的游标对象传递到 League\Fractal\Resource\Collection::setCursor() 方法即可。

Fractal 为我们提供了一个非常基础的游标类 League\Fractal\Pagination\Cursor ,我们基于它在 Laravel 框架中演示如果通过游标返回分页结果:

Route::get('fractal/cursor', function (Request $request) {
    $current = $request->input('current');
    $previous = $request->input('previous');
    $limit = $request->input('limit', 10);

    if ($current) {
        $tasks = \App\Task::where('id', '>', $current)->take($limit)->get();
    } else {
        $tasks =\App\Task::take($limit)->get();
    }

    $next = $tasks->last()->id;
    $cursor = new \League\Fractal\Pagination\Cursor($current, $previous, $next, $tasks->count());

    $resource = new \League\Fractal\Resource\Collection($tasks, new \App\Transformers\TaskTransformer());
    $resource->setCursor($cursor);

    $fractal = new \League\Fractal\Manager();
    return $fractal->createData($resource)->toJson();
});

通过游标获取分页结果类似限定查询,不会统计项目总数,在性能要优于分页器,上述分页结果返回响应数据格式如下:

使用 Dingo API 扩展包快速构建 Laravel RESTful API(四) —— 转化器篇(上):Fractal 及其使用入门

关于 Fractal 的使用我们就简单介绍到这里,更多细节请参考 官方文档 ,下一篇我们将介绍 Dingo API 中如何基于 Fractal 实现转化器以及转化器在 Dingo API 中的使用。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 我们


推荐阅读
  • 在高并发需求的C++项目中,我们最初选择了JsonCpp进行JSON解析和序列化。然而,在处理大数据量时,JsonCpp频繁抛出异常,尤其是在多线程环境下问题更为突出。通过分析发现,旧版本的JsonCpp存在多线程安全性和性能瓶颈。经过评估,我们最终选择了RapidJSON作为替代方案,并实现了显著的性能提升。 ... [详细]
  • ElasticSearch 集群监控与优化
    本文详细介绍了如何有效地监控 ElasticSearch 集群,涵盖了关键性能指标、集群健康状况、统计信息以及内存和垃圾回收的监控方法。 ... [详细]
  • 本文介绍了Elasticsearch (ES),这是一个基于Java开发的开源全文搜索引擎。ES通过JSON接口提供服务,支持分布式集群管理和索引功能,特别适合大规模数据的快速搜索与分析。 ... [详细]
  • 本文探讨了为何采用RESTful架构及其优势,特别是在现代Web应用开发中的重要性。通过前后端分离和统一接口设计,RESTful API能够提高开发效率,支持多种客户端,并简化维护。 ... [详细]
  • 本文介绍了如何利用Vue.js中的Axios库将数组数据发送至Laravel后端,并正确地将这些数据存储到数据库中。 ... [详细]
  • mysql数据库json类型数据,sql server json数据类型
    mysql数据库json类型数据,sql server json数据类型 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 深入解析 Vue 中的 Axios 请求库
    本文深入探讨了 Vue 中的 Axios 请求库,详细解析了其核心功能与使用方法。Axios 是一个基于 Promise 的 HTTP 客户端,支持浏览器和 Node.js 环境。文章首先介绍了 Axios 的基本概念,随后通过具体示例展示了如何在 Vue 项目中集成和使用 Axios 进行数据请求。无论你是初学者还是有经验的开发者,本文都能为你解决 Vue.js 相关问题提供有价值的参考。 ... [详细]
  • 掌握PHP编程必备知识与技巧——全面教程在当今的PHP开发中,了解并运用最新的技术和最佳实践至关重要。本教程将详细介绍PHP编程的核心知识与实用技巧。首先,确保你正在使用PHP 5.3或更高版本,最好是最新版本,以充分利用其性能优化和新特性。此外,我们还将探讨代码结构、安全性和性能优化等方面的内容,帮助你成为一名更高效的PHP开发者。 ... [详细]
  • Ceph API微服务实现RBD块设备的高效创建与安全删除
    本文旨在实现Ceph块存储中RBD块设备的高效创建与安全删除功能。开发环境为CentOS 7,使用 IntelliJ IDEA 进行开发。首先介绍了 librbd 的基本概念及其在 Ceph 中的作用,随后详细描述了项目 Gradle 配置的优化过程,确保了开发环境的稳定性和兼容性。通过这一系列步骤,我们成功实现了 RBD 块设备的快速创建与安全删除,提升了系统的整体性能和可靠性。 ... [详细]
  • 深入解析动态代理模式:23种设计模式之三
    在设计模式中,动态代理模式是应用最为广泛的一种代理模式。它允许我们在运行时动态创建代理对象,并在调用方法时进行增强处理。本文将详细介绍动态代理的实现机制及其应用场景。 ... [详细]
  • 本文深入探讨了MySQL中常见的面试问题,包括事务隔离级别、存储引擎选择、索引结构及优化等关键知识点。通过详细解析,帮助读者在面对BAT等大厂面试时更加从容。 ... [详细]
  • 远程过程调用(RPC)是一种允许客户端通过网络请求服务器执行特定功能的技术。它简化了分布式系统的交互,使开发者可以像调用本地函数一样调用远程服务,并获得返回结果。本文将深入探讨RPC的工作原理、发展历程及其在现代技术中的应用。 ... [详细]
  • 本文探讨了Web API 2中特性的路由机制,特别是如何利用它来构建RESTful风格的URI。文章不仅介绍了基本的特性路由使用方法,还详细说明了如何通过特性路由进行API版本控制、HTTP方法的指定、路由前缀的应用以及路由约束的设置。 ... [详细]
  • 如何撰写适应变化的高效代码:策略与实践
    编写高质量且适应变化的代码是每位程序员的追求。优质代码的关键在于其可维护性和可扩展性。本文将从面向对象编程的角度出发,探讨实现这一目标的具体策略与实践方法,帮助开发者提升代码效率和灵活性。 ... [详细]
author-avatar
Allen
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有