我们使用 Laravel 开发 API 接口时,一般可能会使用 Dingo API,Dingo API 默认使用的是 thephpleague-fractal。
即使我们不使用 Dingo API,开发 API 接口时,我们也可以使用 thephpleague-fractal。
接下来,让我们简单了解下 thephpleague-fractal,到目前为止,我也不太清楚 thephpleague-fractal 如何使用,下面开始学习笔记github 地址:https://github.com/thephpleague/fractal官网:http://fractal.thephpleague.com/翻译:简介(Introduction):Fractal 是什么?Fractal 为复杂的数据输出提供了一个表示和转换层,就像在处理的 RESTful APIs 数据一样,并且可以很好地与 JSON 一起使用。可以将其视为 JSON/YAML 等的视图层。在构建 API 时,我们通常只是从数据库获取数据,并将其传递给 json_encode()。对于 '普通' API 而言,这可能是可以接受的,但如果它们被公众使用,或被移动应用使用,那么这将很快导致输出不一致。Fratal 要实现的目标:在源数据和输出之前创建一个 'barrier',这样做的目的是,让架构的更改不会影响用户使用。系统性的数据的类型转换,可以避免 foreach() 循环和 (bool) 一切对于复杂数据结构,引入依赖关系(a.k.a - also known as - 也叫做,嵌入(embedding)、嵌套(nesting)或 单边加载(side-loading))使用 HAL 和 JSON-API 等标准,但也允许自定义序列化支持数据结果分页,适用于小型和大型数据集通常会简化 '非普通' API 的输出数据的复杂度简单示例(Simple Example):简单起见,下面的示例将几个不同的部分放在一起,看起来就像一个文件。实际开发中,我们应该将 manager 初始化、数据集合和 JSON 转换,放置到我们应用中的不同文件。<?phpuse League\Fractal\Manager;use League\Fractal\Resource\Collection;// 创建一个顶层实例$fractal = new Manager();// 从某种来源获取数据// 历史上出于性能原因,SQL 引擎的大多数 PHP 扩展返回的都是 string 类型。// 我们稍后会修复它,目前以下面这个 array 替代$books = [['id' => '1','title' => 'Hogfather','yr' => '1998','author_name' => 'Philip K Dick','author_email' => 'philip@example.org',],['id' => '2','title' => 'Game Of Kill Everyone','yr' => '2014','author_name' => 'George R. R. Satan','author_email' => 'george@example.org',]];// 将数组(或集合)传递给一个资源,该资源也有一个 'Transformer'// 该 'Transformer' 可以是一个回调或一个 Transformer 对象的新实例// 我们在这里限制回调函数的变量类型是 'array',因为上面 $books 的每个条目都是一个 array 类型$resource = new Collection($books, function(array $book){return ['id' => (int) $book['id'],'title' => $book['title'],'year' => (int) $book['yr'],'author' => ['name' => $book['author_name'],'email' => $book['author_email'],],'links' => ['rel' => 'self','uri' => '/books/' . $book['id'],],];});// 将返回的资源,转换为一个结构化数组(方便 XML 视图或自动 YAML 转换)$array = $fractal->createData($resource)->toArray();// 将返回的资源,转换为 JSON 字符串echo $fractal->createData($resource)->toJson();值得注意的是,回调函数是使用真实 'Transformers' 的一个非常不好的替代。Transformers 允许我们重用转换器,并且使得控制器代码更简洁。安装(Installation):系统依赖:PHP >= 5.4.0,推荐使用 PHP 的最新稳定版Composer:composer install league/fractal几乎所有的现代框架都默认支持了 Composer 自动加载,对于其他情况,确保引入以下文件:<?php// 引入 Composer 自动加载require 'vendor/autoload.php'Going Solo(单独使用):也可以不使用 Composer,通过注册 autoloader 函数,来使用 Fractal:spl_autoload_register(function ($class) {$prefix = 'League\\Fractal\\';$base_dir = __DIR__ . '/src/';$len = strlen($prefix);if (strncmp($prefix, $class, $len) !== 0) {// no, move to the next registered autoloaderreturn;}$relative_class = substr($class, $len);$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';if (file_exists($file)) {require $file;}});或使用任意其他的 PSR-4 兼容的 autoloader词汇表(Glossary):CursorCursor 是一种非智能形式的分页,它不需要知道数据库中数据的总数。这样就不知道下一页是否存在,意味着,API 客户端需要一直发送 HTTP 请求,直到没有数据(404)Include数据通常与其他数据关联。例如:用户有帖子、帖子有回复、回复属于帖子等。当在 RESTful APIs 里表示时,这些数据通常 "included"(插入、嵌套等) 到资源中。transformer 可以包含一个 includePosts() 方法,该方法希望返回一个资源,以便将返回的资源放到父资源中。ManagerFractal 有一个命名为 Manager 的类,负责维护一个记录,记录着哪些嵌入数据已经被请求过,并递归地将嵌套数据转换为 array、JSON、YAML等。Pagination分页是将内容划分为多个页面的过程,与 Fractal 相关的过程可分为两种方式:Cursor 和 Pagination。PaginatorPaginator 是一种智能形式的分页,它需要知道数据库中数据的总数。它会添加一个 'paginator' 数据到响应的元数据,包含了上一页和下一页的链接。Resource资源是一个对象,充当着通用数据的包装器。每个资源都附加一个转换器,以便最终转换为准备好进行序列化和输出(对数据进行转换,最后返回一个资源,然后调用不同的序列化方法,最终输出)。SerializerSerializer 是以某种方式构建你的转换后的数据。API 有许多输出结构,流行的 2 个结构是:HAL 和 JSON-API。Twitter 和 Facebook 输出的数据互不相同,Google 也不同。Serializer 让我们在 Transformers 上可进行最小的修改,然后在不同的输出格式之间切换。TransformerTransformers 可以是类,或者匿名函数,负责获取一个资源数据实例,并将其转换为一个基本数组。这个过程用来处理我们的数据存储,避免对象关系阻抗不匹配,并允许我们根据需要,从不同的数据存储中将各种元素结合起来。数据来自这些不同的数据存储,并处理成更易于管理的、准备好的可进行序列化的数据格式。Resources资源是表示数据的对象,并且也涉及到 Transformer 的相关知识,Transformer 是指明如何输出数据的一个对象或回调函数。存在两种类型的资源:League\Fractal\Resource\Item - 单个资源,可能是数据存储中的一个条目League\Fractal\Resource\Collection - 一个资源集合Item 和 Collection 构造函数,接收你希望发送给它的任意类型的数据作为第一个参数,以及一个 'Transformer' 作为第二个参数。下面的示例,使用的是回调函数 Transformer,而不是创建 Transformer 类,纯粹是为了演示(项目中,推荐使用 Transformer 类)Item 示例:use Acme\Model\Book;use League\Fractal;$book = Book::find($id);$resource = new Fractal\Resource\Item($book, function(Book $book){return ['id'      => (int) $book->id,'title'   => $book->title,'year'    => (int) $book->yr,'links'   => ['self' => '/books/'.$book->id,]];});Collection 示例:use Acme\Model\Book;use League\Fractal;$books = Book::all();$resource = new Fractal\Resource\Collection($books, function(Book $book) {return ['id'    => (int) $book->id,'title' => $book->title,'year'  => (int) $book->yr,'links' => ['self' => '/books/'.$book->id,]];});在这个示例中,$books 是 Acme\Model\Book 实例的一个数组,或一个实现了 ArrayIterator 的集合类SerializersSerializer 是以某种方式构建你的转换后的数据。API 有许多输出结构,流行的 2 个结构是:HAL 和 JSON-API。Twitter 和 Facebook 输出的数据互不相同,Google 也不同。这些 Serializers 之间的大多数差异是,如何定义数据的命名空间。Serializer 让我们在 Transformers 上可进行最小的修改,然后在不同的输出格式之间切换。Fractal 的一个非常基本的用法如下所示,可能已经在其他章节看到过了:use Acme\Model\Book;use Acme\Transformer\BookTransformer;use League\Fractal\Manager;use League\Fractal\Resource\Item;use League\Fractal\Serializer\DataArraySerializer;$manager = new Manager();$manager->setSerializer(new DataArraySerializer());// 某种 ORM 调用$book = Book::find($id);// 从数据中创建一个资源并运行所有 transformers$resource = new Item($book, $new BookTransformer(), 'book');// 传入资源,生成最终数据$manager->createData($resource)->toArray();// 输出:['data' => ['id' => 'Foo','title' => 'Foo','year' => 1991,],];这里的新功能是 $manager->setSerializer(new DataArraySerializer()) 部分。DataArraySerializer 是 Fractal 中默认的 Serializer 的名字,还有更多的 Serializers。DataArraySerializer该序列化并不符合每个人的口味,因为它给输出添加了一个 'data' 的命名空间:// Item['data' => ['foo' => 'bar'],];// Collection['data' => [['foo' => 'bar']],];这很方便,因为它允许在 Items 和 Collections 中为元数据(像:pagination 或 totals)预留空间。// 带有 meta 的 Item['data' => ['foo' => 'bar'],'meta' => [...]];// 带有 meta 的 Collection['data' => [['foo' => 'bar']],'meta' => [...]];使用 'data' 命名空间,非常适合元数据(meta)和引入资源(included resources)。意味着也可以为那些引入资源也可以添加元数据。// 引入资源的 Item 使用 meta['data' => ['foo' => 'bar'// 这就是一个 '引入资源',它自身也可以添加 'meta''comments' => ['data' => [...],'meta' => [...]]],];ArraySerializer有时候,我们也想为 items 移除 'data' 命名空间,这可以通过使用 ArraySerializer 来实现。除了 items 的命名空间之外,大体相同。Collections 保留了 'data' 命名空间,以避免在添加元数据(meta data)时混淆 JSON。use League\Fractal\Serializer\ArraySerializer;$manager->setSerializer(new ArraySerializer());// Item['foo' => 'bar'];// Collection 仍然具有 'data' 命名空间['data' => ['foo' => 'bar']];元数据(meta data)对于 items 比较合适,但对于 Collection 有一点混淆:// 带有 meta 的 Item['foo' => 'bar''meta' => [...]];// 带有 meta 的 Collection[['foo' => 'bar']'meta' => [...]];带有 meta data 的 Collection 返回的json,会自动给个 '0' 下标,有点不友好添加一个命名的键到列表,混淆了 JSON:{“0”:{“foo”:”bar”},”meta”:{}}这里的 "0",是因为你不能混合索引键和非索引键(xxx...xxx不好翻译)这就是为什么不建议使用 ArraySerializer,但是如果你不使用元数据,那么...继续JsonApiSerializer这是 JSON-API 标准(v1.0)的表示。它实现了最常用的功能,例如:Primary DataResource ObjectsResource Identifier ObjectsCompound DocumentsMeta InformationLinksRelacationshipsInclusion of Related Resources目前还未包含的功能:Sparse FieldsetsSortingPaginationFiltering由于 Fractal 是一个输出数据结构的库,serializers 只能转换 HTTP 响应的内容。因此,以下功能只能由我们自己实现:Content NegotiationHTTP Response CodesError Objects更多信息请参阅官方 JSON API 规范JSON API 对于我们的资源需要一个 Resource Key,正如每个对象的 id 一样。(就是需要一个标识)use League\Fractal\Serializer\JsonApiSerializer;$manager->setSerializer(new JsonApiSerializer());// 注意,第三个参数就是 Resource Key(必须给出第三个参数,标识当前 resource)$resource = new Item($book, new JsonApiBookTransformer(), 'books');$resource = new Collection($books, new JsonApiBookTransformer(), 'books');Resource Key 用于为其指定一个命名空间:// Item['data' => ['type' => 'books',     // 这里就是我们传递的 resource key'id' => 1,'attributes' => ['foo' => 'bar'],],];// Collection['data' => [['type' => 'books',     // 这里就是我们传递的 resource key'id' => 1,'attributes' => ['foo' => 'bar'],]],];就像 DataArraySerializer,非常适用于元数据:// 带有 meta 的 Item['data' => ['type' => 'books','id' => 1,'attributes' => ['foo' => 'bar']],'meta' => [...]];// 带有 meta 的 Collection['data' => [['type' => 'books','id' => 1,'attributes' => ['foo' => 'bar']]],'meta' => [...]];给 item 响应添加一个 '资源'(通过 include 进来的资源),如下所示:// Item with a related resource['data' => ['type' => 'books','id' => 1,'attributes' => ['foo' => 'bar'],'relationships' => ['author' => ['data' => ['type' => 'people','id' => '1',]]]],'included' => [['type' => 'people','id' => 1,'attributes' => ['name' => 'Dave']]]];如果你想启用 links 支持,只需要在你的 Serializer 上设置一个 baseUrluse League\Fractal\Serializer\JsonApiSerializer;// 设置 $baseUrl$baseUrl = 'http://example.com';$manager->setSerializer(new JsonApiSerializer($baseUrl));同样的资源,添加 links 后,如下所示:// Item with a related resource and links support['data' => ['type' => 'books','id' => 1,'attributes' => ['foo' => 'bar'],'links' => ['self' => 'http://example.com/books/1'],'relationships' => ['author' => ['links' => ['self' => 'http://example.com/books/1/relationships/author','related' => 'http://example.com/books/1/author'],'data' => ['type' => 'people','id' => '1',]]]],'included' => [['type' => 'people','id' => 1,'attributes' => ['name' => 'Dave'],'links' => ['self' => 'http://example.com/people/1']]]];自定义 Serializers我们可以实现 SerializerAbstract 来制作自己的 Serializersuse Acme\Serializer\CustomSerializer;$manager->setSerializer(new CustomSerializer());Serializers 的结构将在某些时候发生改变,来允许以不同方式处理 items 和 collections,并改进 side-loading 逻辑。密切关注更改日志,但不要害怕创建更改日志。Transformer在 Resource 章节,展示了回调函数方式的 transformers,但是使用上受限:<?phpuse Acme\Model\Book;use League\Fractal; $books = Book::all();$resource = new Fractal\Resource\Collection($books, function(Book $book) {return ['id'      => (int) $book->id,'title'   => $book->title,'year'    => $book->yr,'author'  => ['name'  => $book->author_name,'email' => $book->author_email,],'links'   => [['rel' => 'self','uri' => '/books/'.$book->id,]]];});在某些情况下,使用这种方式是比较便捷的,但大多数数据需要在多个位置进行多次转换, 因此创建 transformers 类的方式,可以减少代码重复Transformer 类为了重用 transformers(推荐),可以定义、实例化、传递 类,来代替回调函数。这些类必须继承 League\Fractal\TransformerAbstract,并且至少包含名称为 transform() 的方法。方法声明可以接受各种类型的输入,就像回调函数的参数一样:<?phpnamespace Acme\Transformer;use Acme\Model\Book;use League\Fractal;class BookTransformer extends Fractal\TransformerAbstract{public function transform(Book $book){return ['id'      => (int) $book->id,'title'   => $book->title,'year'    => (int) $book->yr,'links'   => [['rel' => 'self','uri' => '/books/'.$book->id,]],];}}一旦 Transformer 类被定义,就可以将其作为实例,传递给 resource 构造函数<?phpuse Acme\Transformer\BookTransformer;use League\Fractal;$resource = new Fractal\Resource\Item($book, new BookTransformer);$resource = new Fractal\Resource\Collection($books, new BookTransformer);引入数据(Including Data)此刻我们定义的 transformer,主要只提供了一个用来处理数组转换的方法,将源数据(或从模型返回的任意内容)转换为一个简单的数组。以智能方式来引入数据,这个问题很难解决,因为数据有各种各样的关系。许多开发人员尝试在 '不发送过多的 HTTP 请求' 和 '不下载超出需要的更多数据' 之间寻找一个完美平衡,同时也兼顾灵活性。仍旧使用 book 的示例,BookTransformer,我们可能希望规范化我们的数据库,并将2个 author_* 字段取出,放入它们自己的表中。这个引入可以是可选的,以减少 JSON 响应,定义如下:<?php namespace App\Transformer;use Acme\Model\Book;use League\Fractal\TransformerAbstract;class BookTransformer extends TransformerAbstract{/*** List of resources possible to include** @var array*/protected $availableIncludes = ['author'];/*** Turn this item object into a generic array** @return array*/public function transform(Book $book){return ['id'    => (int) $book->id,'title' => $book->title,'year'    => (int) $book->yr,'links'   => [['rel' => 'self','uri' => '/books/'.$book->id,]],];}/*** Include Author** @return \League\Fractal\Resource\Item*/public function includeAuthor(Book $book){$author = $book->author;return $this->item($author, new AuthorTransformer);}}这些引入是可用的,但不能被请求,除非 Manager::parseIncludes() 方法被调用:<?phpuse League\Fractal;$fractal = new Fractal\Manager();if (isset($_GET['include'])) {$fractal->parseIncludes($_GET['include']);}有了这个设置,引入可以做一些很棒的事情。如果一个客户端应用请求的 URL 为 '/books?include=author',那么它们将会在响应中看到 author 的数据。这些引入也可以以 '.' 符号被嵌套,来引入其他资源中的资源。例如:/books?include=author,publishers.somethingelse注意:publishers 也会被引入,somethingelse 嵌套在它下面。 可以当成是 publishers,publishers.somethingelse 的简写嵌套层级限制为 10 级。使用 Manager::setRecursionLimit(5) 方法,传递你喜欢的任意数字,可以增加或减少嵌套的层级,指定到我们设置的层级。可能 4 或 5 会是一个比较好的层级,这取决于 API。默认引入正如可选的引入一样,默认引入定义在 transformer 的 $defaultIncludes 属性上protected $defaultIncludes = ['author'];public function includeAuthor(Book $book){$author = $book->author;return $this->item($author, new AuthorTransformer);}输出上看起来是相同的,就好像用户请求了 '?include=author'(每个 URL 请求不需要传入 'include=author',默认程序就会添加上该引入)排除引入Manager::parseExcludes() 方法可用于在单个响应中忽略默认引入<?phpuse League\Fractal;$fractal = new Fractal\Manager();$fractal->parseExcludes('author'); 同样的 '.' 符号也可用于 Manager::parseExcludes()对于排除引入,使用了 '.' 符号,只会排除路径中的深层次的嵌套,不会排除嵌套的父级为了省略 BookTransformer 的默认 author 引入和 AuthorTransformer 默认的嵌套的 editor 引入,可以传递:author.editor,author。因为单独传递 author.editor,只会从响应中省略 author.editor 资源。解析后的排除,最终决定了是否会在响应中看到某个引入。这意味着,即使包含在 Manager::parseIncludes() 中的引入,也可以被 Manager::parseExcludes() 忽略掉。(其实就是我们经常看到的:否定会比肯定更优先,Apache 的 Deny 优先 Allow)引入参数(Include Parameters)在引入其他资源时, 语法可用于为引入方法提供额外的参数。这些参数是在 URL 中构造的:?include=comments:limit(5|1):order(created_at|desc)此语法将通过 'League\Fractal\ParamBag' 对象进行解析和提供,作为第二个参数传递到引入方法中。<?phpuse League\Fractal\ParamBag;// ... transformer stuff ...private $validParams = ['limit', 'order'];/*** Include Comments** @param Book $book* @param \League\Fractal\ParamBag|null* @return \League\Fractal\Resource\Item*/public function includeComments(Book $book, ParamBag $params = null){if ($params === null) {return $book->comments;}// Optional params validation$usedParams = array_keys(iterator_to_array($params));if ($invalidParams = array_diff($usedParams, $this->validParams)) {throw new \Exception(sprintf('Invalid param(s): "%s". Valid param(s): "%s"', implode(',', $usedParams), implode(',', $this->validParams)));}// Processinglist($limit, $offset) = $params->get('limit');list($orderCol, $orderBy) = $params->get('order');$comments = $book->comments->take($limit)->skip($offset)->orderBy($orderCol, $orderBy)->get();return $this->collection($comments, new CommentTransformer);}参数有一个名称,以及多个值,总是返回一个数组,即便只有一个参数,返回的也是一个数组。可以通过 get() 方法来访问,也可以通过数组的形式来访问。因此,$params->get('limit') 和 $params['limit'] 是等效的贪婪加载 vs 懒加载(Eager-Loading vs Lazy-Loading)上面的示例正好使用 ORM 的懒加载功能。懒加载可能非常慢,因为每次转换一个条目时,它都不得不关闭并查找其他数据,导致大量 SQL 请求。通过检查 $_GET['include'] 的值,可以很容易地使用贪婪加载,并且使用它来生成一个关系列表,来贪婪加载 ORM(不懂...)Pagination使用大型数据集时,为端点提供分页选项,显然是很有意义的,否则获取数据会变得很慢。为避免将自己的分页输出写入每个端点,Fractal 提供了 2 种解决方案:PaginatorCursor使用 PaginatorPaginator 提供了有关结果集的更多信息,包括:总数,上一页/下一页 链接,只有在有更多可用数据时才显示。此智能分页的代价是,每次调用必须计算数据的总条数对于某些数据集,这可能不是问题,但对于其他一些肯定是。如果只考虑速度,请考虑使用 Cursor 替代。创建 Paginator 对象,必须实现 'League\Fractal\Pagination\PaginatorInterface' 以及它的指定方法。实例化的对象之后必须传递给 League\Fractal\Resource\Collection::setPaginator() 方法Fractal 目前支持以下适配器:Laravel 的 illuminate/pagination 包作为 League\Fractal\Pagination\IlluminatePaginatorAdapterpagerfanta/pagerfanta 包作为 League\Fractal\Pagination\PagerfantaPaginatorAdapterPhalcon Paginator(phalcon/cphalcon) 作为 League\Fractal\Pagination\PhalconFrameworkPaginatorAdapterZend Framework 的 zendframework/zend-paginator 包作为 League\Fractal\Pagination\ZendFrameworkPaginatorAdapterLaravel 分页我们可以使用 Laravel 的 Eloquent 或 Query Builder 的 paginate() 方法来达到分页目的:use League\Fractal\Resource\Collection;use League\Fractal\Pagination\IlluminatePaginatorAdapter;use Acme\Model\Book;use Acme\Transformer\BookTransformer;$paginator = Book::paginate();$books = $paginator->getCollection();$resource = new Collection($books, new BookTransformer());$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));Phalcon 分页看文档Symfony 分页使用的是 'pagerfanta/pagerfanta' 包看文档在分页链接中总包含已经存在的查询字符串在上面的例子中,上一页和下一页,将简单提供一个 '?page=#',而忽略了所有其他已存在的查询字符串。为了在这些链接中自动包含所有的查询字符串值,我们可以进行如下修改:use Acme\Model\Book;$year = Input::get('year');$paginator = Book::where('year', '=', $year)->paginate(20);// 注意:// 使用 '$appends()' 追加上除了 'page' 的其他查询字符串$queryParams = array_diff_key($_GET, array_flip(['page']));$paginator->appends($queryParams);$paginatorAdapter = new IlluminatePaginatorAdapter($paginator);$resource->setPaginator($paginatorAdapter);使用 Cursor当我们拥有大量数据集,并且运行 'SELECT COUNT(*) FROM ...' 并不需要,我们需要一种合适的获取数据的方法。其中一种方法是使用 Cursor,向后端程序指示从哪里开始获取数据。我们可以使用 'League\Fractal\Resource\Collection::setCursor()' 方法来在集合上设置一个新的 cursor。Cursor 必须实现 'League\Fractal\Pagination\CursorInterface' 以及它指定的方法Fractal 目前支持一个非常基本的适配器: League\Fractal\Pagination\Cursor。它真的很容易使用:use Acme\Model\Book;use Acme\Transformer\BookTransformer;use League\Fractal\Pagination\Cursor;use League\Fractal\Resource\Collection;$currentCursor = Input::get('cursor', null);$previousCursor = Input::get('previous', null);$limit = Input::get('limit', 10);if ($currentCursor) {$books = Book::where('id', '>', $currentCursor)->take($limit)->get();} else {$books = Book::take($limit)->get();}$newCursor = $books->last()->id;$cursor = new Cursor($currentCursor, $previousCursor, $newCursor, $books->count());$resource = new Collection($books, new BookTransformer);$resource->setCursor($cursor);上面的示例是基于 Laravel 的 'illuminate\database' 包,但我们可以在任意我们想要的环境中执行此操作。上面的 cursor 正好是 id 字段,但是它也可以是很简单的偏移数字。无论选择什么来表示 cursor,可以使用 base64_encode() 和 base64_decode() 来编码 cursor 值,来确保 API 用户不会使用 cursor 来做任何太多聪明的事情。他们只需要将 cursor 传递给新的 URL,不需要做其他任何计算。Cursor 使用示例:GET /books?cursor=5&limit=5{"books": [{ "id": 6 },{ "id": 7 },{ "id": 8 },{ "id": 9 },{ "id": 10 }],"meta": {"cursor": {"previous": null,"current": 5,"next": 10,"count": 5}}}在下一个请求中,我们向前移动 cursor设置 cursor 为上一个请求中的 next 值设置 previous 为上一个请求中的 current 值limit 是可选的,我们可以设置为上一个请求中的 count 值,来保持相同条数的数据GET /books?cursor=10&previous=5&limit=5{"books": [{ "id": 11 },{ "id": 12 },{ "id": 13 },{ "id": 14 },{ "id": 15 }],"meta": {"cursor": {"previous": 5,"current": 10,"next": 15,"count": 5}}}

laravel 安装 thephpleague/fractal,以及 thephpleague/fractal 中文翻译相关推荐

  1. laravel安装-中文语言包

    一.laravel 安装 "overtrue/laravel-lang" 这个扩展包直接把语言变成了中文 composer require overtrue/laravel-lan ...

  2. laravel安装笔记 (转)

    一.安装composer 安装之前将\php\php.ini文件中的php_openssl.dll扩展库开启,否则composer在安装过程中会出现错误提示. (我在安装过程中发现apache目录下的 ...

  3. 后盾网lavarel视频项目---Laravel 安装代码智能提示扩展「laravel-ide-helper」

    后盾网lavarel视频项目---Laravel 安装代码智能提示扩展「laravel-ide-helper」 一.总结 一句话总结: laravel-ide-helper作用是:代码提示 larav ...

  4. laravel 安装后500错误

    laravel 安装之后浏览器打开空白显示500错误. 1.storage权限没问题,域名解析到public  没问题 2.open_basedir 设置错误设置到了public了.去掉即可. < ...

  5. 雷林鹏分享:Laravel 安装

    前面我们介绍我了 composer安装,这里我们接着来介绍 Laravel框架的安装. 这里我们安装的是laravel 4 项目下载地址:https://github.com/laravel/lara ...

  6. laravel5.5 尝试使用laravel安装器安装(失败) 最后还是用的composer。。。

    参考连接 http://laravelacademy.org/post/7620.html 显示原文 [ Laravel 5.5 文档 ] 快速入门 -- 安装配置篇  Posted on 2017年 ...

  7. laravel 安装

    laravel 学院:http://laravelacademy.org 入门教程:https://lvwenhan.com/laravel/... 中文网教程:http://www.php.cn/ ...

  8. MATLAB安装时为英文如何切换中文

    MATLAB安装时为英文如何切换中文 MATLAB安装问题 问题描述 2018b及以上版本的MATLAB安装时,其中英文模式会根据电脑所在区域环境进行配置.若电脑所在区域环境不在中国,则MATLAB初 ...

  9. 如何下载安装和使用 Office 2016的中文语言包?

    如何下载安装和使用 Office 2016的中文语言包? ©Lander Zhang 专注外企按需IT基础架构运维服务,IT Helpdesk 实战培训践行者 https://blog.51cto.c ...

  10. laravel安装的几种方法总结

    Laravel安装方法大的来分可以分为利用Composer安装和一键安装包安装. 先来说一下最简单的安装方式,利用一键安装包来安装Laravel. 为了方便初学者学习Laravel有大神在网上提供了一 ...

最新文章

  1. 作为程序员,你评估工作量留 buffer 吗?
  2. sqlserver中pivot的使用
  3. 如何启动mongoDB并用Robo 3T连接
  4. 修改linux系统中自带的jdk
  5. 传智播客全栈_播客:从家庭学生到自学成才的全栈开发人员
  6. 友盟页面访问路径全量统计功能上线啦!
  7. javaweb之mysql数据库
  8. HTML5 -- WebSocket
  9. 转--大话session
  10. 开源的WebKit 浏览器引擎受多个漏洞影响,可导致 RCE 后果
  11. 新建samba配置步骤
  12. 【微信小程序】小程序代码基本组成结构
  13. python程序设计实验报告答案大全_Python程序设计实验三
  14. JDK8 toMap之key重复报Duplicate key xxxx异常解决
  15. mysql查询周数_MySQL根据年份的周数获取该周起始时间
  16. u8云服务器系统管理,用友u8连云服务器
  17. HTML 表单 (form) 的作用解释
  18. 路径穿越(Path Traversal)详解
  19. 撞了南墙要记得回头看看
  20. 【32位系统与64位系统可访问内存的大小】

热门文章

  1. 选修课报名抢座小程序 毕业设计毕业论文 开题报告和效果图(基于微信小程序毕业设计题目选题课题)
  2. Python : Pygame函数大全
  3. GEE学习:影像数据集理解与显示
  4. Lucene实现分词
  5. c语言 进击的贪吃蛇(easyx大作业版)
  6. 微信小程序拖拽排序列表
  7. gmsh编译error C2440: “初始化”: 无法从“std::_Tree_iterator_Mytree”转换为“std::_Tree_iterator_Mytree”
  8. 记录腾讯云服务器被植入pnscan挖矿病毒折磨的一天
  9. Spring Tool Suite(简称STS)针对SimpleDateFormat.pase函数的实参值不做检验,异常直接默认值之
  10. 2023年会展行业研究报告