Java API设计实战指南:打造稳健、用户友好的API
具有嵌套关系的可重用API资源——Laravel5.5
本文内容主要围绕在 Laravel 5.5 中使用 API 开发的重要步骤,着重介绍如何利用 Laravel 的 API 资源(Resource)和控制器(Controller)进行多因素身份验证(MFA)。尤其强调了利用 Resource::collection 方法简化数据提供过程,以及对比 Fractal 和 Laravel 的资源处理方式。
注:本文受到Laravel创始人Taylor Otwell介绍使用 Laravel5.5 开发API时如何替换
Fractal 的启发。
1. 安装一个干净的 Laravel 5.5 项目
· 使用 Composer 命令 composer create-project laravel/laravel responses dev-develop
来创建一个 Laravel 5.5 项目。这个命令会从 Laravel 官方的存储库中下载最新版本的 Laravel 5.5 代码并安装到名为 “responses” 的文件夹中。
· cd responses
: 进入到新创建的 “responses” 文件夹中。
· touch database/database.sqlite
: 创建一个 SQLite 数据库文件,用于存储数据。
php artisan make:model Post -mf
: 创建一个名为 “Post” 的 Eloquent 模型,并生成相应的迁移文件和工厂。
· php artisan make:resource UsersWithPostsResource
: 创建一个名为 “UsersWithPostsResource” 的资源类,用于对用户及其posts进行处理。
· php artisan make:resource PostsResource
: 创建一个名为 “PostsResource” 的资源类,用于对posts进行处理。
· php artisan make:controller UsersController –resource: 创建一个名为 “UsersController” 的控制器,添加了 CRUD(创建、读取、更新、删除)操作的资源路由。
· 修改 .env
文件,使用 SQLite 数据库,并删除其他数据库相关的变量。
· 添加或修改 DB_CONNECTION=sqlite
来指定 Laravel 使用 SQLite 作为数据库连接。
这些步骤旨在建立一个基本的 Laravel 5.5 项目,并做了一些初始化设置,包括创建模型、资源类和控制器,并配置使用 SQLite 作为数据库。
2. 准备数据库
·posts迁移 database/migrations/______create_posts_table.php
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->string('body');
$table->unsignedInteger('user_id');
$table->timestamps();
});
这涉及创建posts模型的数据库表结构。在 database/migrations
目录下的create_posts_table.php
的文件,定义了posts表的字段和结构。这个文件包含了使用 Laravel 的迁移(Migration)功能创建数据库表的代码。
· posts工厂database/factories/PostFactory.php
<?php
use Faker\Generator as Faker;
$factory->define(App\Post::class,
function (Faker $faker) {
return [
'title' => $faker->sentence,
'body' => $faker->paragraph,
'user_id' => function () {
return factory(\App\User::class);
}
];
});
这一步骤是为了创建一个posts的工厂,用于生成测试数据或者用于种子数据填充。在 database/factories
目录下的 PostFactory.php
文件中,你会定义创建posts模型时所用的数据格式和规则。
· 用户拥有posts的关系 app/User.php
public function posts(){return $this->hasMany(Post::class);}
这是在用户模型(User)中定义与posts模型的关系。也就是在 app 目录下的 User.php
文件中,你会定义用户和posts之间的关联关系,比如一对多关系(一个用户有多个posts)或其他关系。
· 避免批量赋值 app/Post.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model{protected $guarded = [];}
在 app
目录下的 Post.php
文件中,通常会有一个模型类,即posts模型(Post)。避免批量赋值是指使用 Laravel 的属性来指定哪些字段可以被批量赋值,以防止不受控制的数据注入。
· 播种数据库
<?php
artisan migrate:freshphp artisan
tinkerfactory(App\Post::class)->times(2)->create();
factory(App\Post::class)->times(2)->create(['user_id' => 1]);
数据库种子用于向数据库中填充测试数据或初始数据。这是在开发或测试阶段常用的操作,可以使用 Laravel 的 Seeder 来填充数据库表,确保数据库中有一些初始数据可用于开发和测试。
3. 设置路由
Route::apiResource('/users', 'UsersController');
4. 重命名资源(修复遗留问题)
之前创建了一个名为 UsersWithPostsResource
的资源。让我们将其重命名为 UsersResource
,并了解如何在以下步骤中重用它。
5. 在控制器内使用API资源
<?php
/**
* Display a listing of the resource.
*
* @param User $user
* @return \Illuminate\Http\Response
*/
public function index(User $user)
{
return UsersResource::collection($user->with('posts')->paginate());
// If you don't want to include the relationship in your response, don't use with()
// return UsersResource::collection($user->paginate());
}
静态 collection
方法将采用要转换的记录集合,并确保为每个记录实例化一个新的UsersResource
。
6. UsersResource类
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class UsersResource extends Resource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
return [
'name' => $this->name,
'email' => $this->email,
'posts' => PostsResource::collection($this->whenLoaded('posts'))
];
}
}
这里的两个关键部分:属性访问器和可选的嵌套转换。
在 Resource
中,可以通过 $this
直接访问模型的属性。这个神奇的功能是通过 DelegatesToResource trait 在基础资源类中实现的。简单来说,这意味着资源类中可以直接使用 $this->attributeName
的方式访问模型中的属性,而不必每次都通过模型实例去获取属性。
能够在资源类中进行关系的转换,但是有条件:如果数据是可用的(已经预加载),就可以进行转换;如果数据尚未加载,可以选择忽略这个转换。这样做有利于避免 N+1 查询问题(在获取关联数据时出现的效率问题),同时可以使用单个资源类处理不同的情况。如果关联数据不可用,资源类会忽略它;反之,如果可用,资源类会将其包含在返回的数据中。
7. Posts Resource
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class PostsResource extends Resource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
return [
'title' => $this->title,
'body' => $this->body
];
}
}
结论
1.简化和逐步进行
本文着重于学习如何使用 Resource::collection 而不是手动实例化类,并将关系数据的提供(或不提供)责任委托给控制器。通过在控制器中简单地移除 with(‘posts’),API 将不再在响应中包含每个用户的posts数据。
2.对比 Fractal 和 Laravel 的资源
本文提到 Fractal 在转换层(Transformer)提供了默认和可用的包含(includes)功能,但是 Laravel 的原生 API 资源更倾向于让控制器处理这个逻辑。毕竟,控制器的工作是理解请求。这暗示着对于数据包含的处理,Laravel 更多地依赖于控制器层面的逻辑,而不是在资源转换层实现。
总体而言,本文聚焦于利用 Laravel 中的 Resource::collection,并强调控制器对于处理数据关系包含的重要性。
参考链接:
First impressions on Laravel API Resources | HackerNoon
具有嵌套关系的可重用 API 资源 — Laravel 5.5 |由 Marco Aurélio Deleu |HackerNoon.com |中等 (medium.com)