appkr / api
Laravel或Lumen项目RESTful HTTP API开发工具
Requires
- league/fractal: 0.16.*
- shrikeh/teapot: 2.3.*
Requires (Dev)
- phpunit/phpunit: 6.1.*
README
索引
1. 关于
为Laravel或/和Lumen项目提供轻量级的RESTful API构建器。
2. 功能
- 为
league/fractal
提供Laravel/Lumen服务提供者。 - 为库提供配置功能。
- 提供制作转换/序列化API响应的简单方法。
- 提供
make:transformer
artisan命令。 - 提供示例,以便用户可以快速复制并粘贴到其项目中。
3. LARAVEL/LUMEN实现示例(如何使用)
3.1. API端点
以Laravel的方式定义RESTful资源路由。
<?php // app/Http/routes.php OR routes/web.php OR routes/api.php Route::group(['prefix' => 'v1'], function () { Route::resource( 'books', 'BooksController', ['except' => ['create', 'edit']] ); });
Lumen不支持RESTful资源路由。您必须逐个定义它们。
<?php // app/Http/routes.php OR routes/web.php OR routes/api.php $app->group(['prefix' => 'v1'], function ($app) { $app->get('books', [ 'as' => 'v1.books.index', 'uses' => 'BooksController@index', ]); $app->get('books/{id}', [ 'as' => 'v1.books.show', 'uses' => 'BooksController@show', ]); $app->post('books', [ 'as' => 'v1.books.store', 'uses' => 'BooksController@store', ]); $app->put('books/{id}, [ 'as' => 'v1.books.update', 'uses' => 'BooksController@update', ]); $app->delete('books/{id}', [ 'as' => 'v1.books.destroy', 'uses' => 'BooksController@destroy', ]); });
3.2. 控制器
以下代码块是针对/v1/books/{id}
端点的控制器逻辑。注意以下代码块中json()
辅助函数和转换器的用法。
<?php // app/Http/Controllers/BooksController.php namespace App\Http\Controllers\V1; use App\Http\Controllers\Controller; use App\Book; use App\Transformers\BookTransformer; use Illuminate\Http\Request; class BooksController extends Controller { public function index() { return json()->withPagination( Book::latest()->paginate(5), new BookTransformer ); } public function store(Request $request) { // Assumes that validation is done at somewhere else return json()->created( $request->user()->create($request->all()) ); } public function show($id) { return json()->withItem( Book::findOrFail($id), new BookTransformer ); } public function update(Request $request, $id) { $book = Book::findOrFail($id); return ($book->update($request->all())) ? json()->success('Updated') : json()->error('Failed to update'); } public function destroy($id) { $book = Book::findOrFail($id); return ($book->delete()) ? json()->success('Deleted') : json()->error('Failed to delete'); } }
4. 安装方法
4.1. Composer。
$ composer require "appkr/api: 1.*"
4.2. 添加服务提供者。
<?php // config/app.php (Laravel) 'providers' => [ Appkr\Api\ApiServiceProvider::class, ];
<?php // boostrap/app.php (Lumen) $app->register(Appkr\Api\ApiServiceProvider::class);
4.3. [可选]发布资产。
# Laravel only $ php artisan vendor:publish --provider="Appkr\Api\ApiServiceProvider"
配置文件位于config/api.php
。
在Lumen中,我们可以手动创建config/api.php
文件,然后在bootstrap/app.php
中激活配置,如下所示。
<?php // bootstrap/app.php (Lumen) $app->register(Appkr\Api\ApiServiceProvider::class); $app->configure('api');
完成!
5. 配置
查看config/api.php
,它具有内联注释。
6. 转换器
6.1. 什么?
有关转换器的更多信息,您可以做什么,以及为什么它是必需的,请参阅此页面。1个转换器对应1个模型是一个最佳实践(例如,为Book
模型提供BookTransformer
)。
6.2. 转换器模板生成器
幸运的是,这个包提供了一个 artisan 命令,可以方便地生成转换器类。
$ php artisan make:transformer {subject} {--includes=}
# e.g. php artisan make:transformer "App\Book" --includes="App\\User:author,App\\Comment:comments:true"
-
subject
- 模型类的字符串名称。 -
includes
- 与主题模型相关的子资源。通过提供此选项,您的API客户端可以控制响应体。请参阅嵌套子资源部分。选项的签名是
--include=Model,eloquent_relationship_methods[,isCollection]
。如果可包含的子资源是集合类型,如示例中的
Book
和Comment
关系,我们提供true
作为选项的第三个值。
注意
在artisan命令中传递命名空间时,我们应该始终使用双反斜杠(
\\
),无需引号。$ php artisan make:transformer App\\Book --includes=App\\User:author,App\\Comment:comments:true
生成的文件将如下所示
<?php // app/Transformers/BookTransformer.php namespace App\Transformers; use App\Book; use Appkr\Api\TransformerAbstract; use League\Fractal; use League\Fractal\ParamBag; class BookTransformer extends TransformerAbstract { /** * List of resources possible to include using url query string. * * @var array */ protected $availableIncludes = [ 'author', 'comments' ]; /** * Transform single resource. * * @param \App\Book $book * @return array */ public function transform(Book $book) { $payload = [ 'id' => (int) $book->id, // ... 'created' => $book->created_at->toIso8601String(), 'link' => [ 'rel' => 'self', 'href' => route('api.v1.books.show', $book->id), ], ]; return $this->buildPayload($payload); } /** * Include author. * This method is used, when an API client request /v1/books?include=author * * @param \App\Book $book * @param \League\Fractal\ParamBag|null $params * @return \League\Fractal\Resource\Item */ public function includeAuthor(Book $book, ParamBag $params = null) { return $this->item( $book->author, new \App\Transformers\UserTransformer($params) ); } /** * Include comments. * This method is used, when an API client request /v1/books??include=comments * * @param \App\Book $book * @param \League\Fractal\ParamBag|null $params * @return \League\Fractal\Resource\Collection */ public function includeComments(Book $book, ParamBag $params = null) { $transformer = new \App\Transformers\CommentTransformer($params); $comments = $book->comments() ->limit($transformer->getLimit()) ->offset($transformer->getOffset()) ->orderBy($transformer->getSortKey(), $transformer->getSortDirection()) ->get(); return $this->collection($comments, $transformer); } }
7. 嵌套子资源
API客户端可以请求带有其子资源的资源。以下示例请求authors
列表。同时,它还请求每个作者的books
列表。它还具有其他参数,表示“我需要为这个作者按最新顺序获取总共3本书,没有任何跳过”。
GET /authors?include=books:limit(3|0):sort(id|desc)
当包含多个子资源时
GET /authors?include[]=books:limit(2|0)&include[]=comments:sort(id|asc) # or alternatively GET /authors?include=books:limit(2|0),comments:sort(id|asc)
在深度递归嵌套的情况下,使用点(.
)。在以下示例中,我们假设发布者模型与somethingelse模型有关联。
GET /books?include=author,publisher.somethingelse
8. API
以下是Appkr\Api\Http\Response
提供的完整响应方法列表。在控制器中制作json响应时非常实用。
8.1. Appkr\Api\Response
- 可用方法
<?php // Generic response. // If valid callback parameter is provided, jsonp response can be provided. // This is a very base method. All other responses are utilizing this. respond(array $payload); // Respond collection of resources // If $transformer is not given as the second argument, // this class does its best to transform the payload to a simple array withCollection( \Illuminate\Database\Eloquent\Collection $collection, \League\Fractal\TransformerAbstract|null $transformer, string|null $resourceKey // for JsonApiSerializer only ); // Respond single item withItem( \Illuminate\Database\Eloquent\Model $model, \League\Fractal\TransformerAbstract|null $transformer, string|null $resourceKey // for JsonApiSerializer only ); // Respond collection of resources with pagination withPagination( \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator, \League\Fractal\TransformerAbstract|null $transformer, string|null $resourceKey // for JsonApiSerializer only ); // Respond json formatted success message // api.php provides configuration capability success(string|array $message); // Respond 201 // If an Eloquent model is given at an argument, // the class tries its best to transform the model to a simple array created(string|array|\Illuminate\Database\Eloquent\Model $primitive); // Respond 204 noContent(); // Respond 304 notModified(); // Generic error response // This is another base method. Every other error responses use this. // If an instance of \Exception is given as an argument, // this class does its best to properly format a message and status code error(string|array|\Exception|null $message); // Respond 401 // Note that this actually means unauthenticated unauthorizedError(string|array|null $message); // Respond 403 // Note that this actually means unauthorized forbiddenError(string|array|null $message); // Respond 404 notFoundError(string|array|null $message); // Respond 405 notAllowedError(string|array|null $message); // Respond 406 notAcceptableError(string|array|null $message); // Respond 409 conflictError(string|array|null $message); // Respond 410 goneError(string|array|null $message); // Respond 422 unprocessableError(string|array|null $message); // Respond 500 internalError(string|array|null $message); // Set http status code // This method is chain-able setStatusCode(int $statusCode); // Set http response header // This method is chain-able setHeaders(array $headers); // Set additional meta data // This method is chain-able setMeta(array $meta);
8.2. Appkr\Api\TransformerAbstract
- 可用方法
<?php // We can apply this method against an instantiated transformer, // to get the parsed query parameters that belongs only to the current resource. // // e.g. GET /v1/author?include[]=books:limit(2|0)&include[]=comments:sort(id|asc) // $transformer = new BookTransformer; // $transformer->get(); // Will produce all parsed parameters: // // [ // // 'limit' => 2 // if not given default value at config // // 'offset' => 0 // if not given default value at config // // 'sort' => 'created_at' // if given, given value // // 'order' => 'desc' // if given, given value // // ] // Alternatively we can pass a key. // $transformer->get('limit'); // Will produce limit parameter: // // 2 get(string|null $key) // Exactly does the same function as get. // Was laid here, to enhance readability. getParsedParams(string|null $key)
8.3. helpers.php
- 可用函数
<?php // Make JSON response // Returns Appkr\Api\Http\Response object if no argument is given, // from there you can chain any public apis that are listed above. json(array|null $payload) // Determine if the current framework is Laravel is_laravel(); // Determine if the current framework is Lumen is_lumen(); // Determine if the current request is for API endpoints, and expecting API response is_api_request(); // Determine if the request is for update is_update_request(); // Determine if the request is for delete is_delete_request();
9. 包含示例
该包捆绑了一组遵循最佳实践的示例。它包括
- 数据库迁移和播种器
- 路由定义、Eloquent 模型和相应的控制器
- FormRequest (仅 Laravel)
- 转换器
- 集成测试
按照指南激活和测试示例。
9.1. 激活示例
取消注释该行。
<?php // vendor/appkr/api/src/ApiServiceProvider.php $this->publishExamples();
9.2. 迁移和播种表
执行以下操作以创建测试表并播种测试数据。强烈建议使用 SQLite,以避免污染您的数据库。
$ php artisan migrate --path="vendor/appkr/api/src/example/database/migrations" --database="sqlite" $ php artisan db:seed --class="Appkr\Api\Example\DatabaseSeeder" --database="sqlite"
9.3. 查看效果
启动服务器。
$ php artisan serve
前往 GET /v1/books
,您应该看到一个格式良好的 JSON 响应。尝试每个路由以熟悉,例如 /v1/books=include=authors
,/v1/authors=include=books:limit(2|0):order(id|desc)
。
9.4. [可选] 运行集成测试
# Laravel
$ vendor/bin/phpunit vendor/appkr/api/src/example/BookApiTestForLaravel.php
# Lumen
$ vendor/bin/phpunit vendor/appkr/api/src/example/BookApiTestForLumen.php
注意
如果您完成了示例的评估,请记住回滚迁移并在
ApiServiceProvider
中重新注释不必要的行。
10. 许可证 & 贡献
MIT 许可证。问题和 PR 总是受到欢迎。
11. 更新日志
v3.0.1
- 支持 Laravel 5.5 的自动包发现(无需在 config/app.php 中添加 ServiceProvider)
v3.0.0
- API 没有变化。
- 将
league/fractal
版本更新到 0.16.0
v2.3.4
- 添加了
jsonEncodeOption
配置。
v2.3.3
- 当包含查询参数无效时,将抛出
Appkr\Api\Http\UnexpectedIncludesParamException
而不是UnexpectedValueException
。
v2.3.0
withCollection()
现在接受Illuminate\Support\Collection
。- 修复了
SimpleArrayTransformer
中的错误。
v2.2.0
- 字段名称根据配置转换为蛇形或驼峰式(
config('api.convert.key')
)。 - 日期格式根据配置进行转换(
config('api.convert.date')
)。
v2.1.0
- 添加了
TransformerAbstract::buildPayload
方法以过滤响应字段列表(向后兼容)。 - Artisan 生成的转换器模板已更改(向后兼容)。
v2.0.0
TransformerAbstract
的 API 已更改。- 删除了通过查询字符串实现的分部分响应功能。相反,我们可以在转换器的
$visible
或$hidden
属性中显式设置要响应的属性列表。
v1.1.0 [有错误]
- 添加了字段分组功能,以方便分部分响应。
- 当 API 客户端传递的参数或值不可接受时,
TransformerAbstract
现在抛出UnexpectedValueException
而不是Exception
。
12. 赞助商
- 感谢 JetBrains 支持 phpStorm IDE。