前言
本文内容主要围绕在 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)
编译:幂简集成