使用 Laravel 的 API 资源功能来构建你的 API

在过去的2年时间里,我一直使用 Fractal 来进行 API 开发。
如果说一个我最希望 Laravel可以增加的功能的话,无疑是方便的数据转换,以便开发更好的 API 接口。
别误会, Fractal 很好用,然而我总是希望能只用框架进行开发。如果可以的话,我尽可能不使用类库!我不喜欢使用第三方类库使得开发复杂化。
在过去的一年里,我喜欢使用一些前端框架如 Vue 和 React 来进行开发。因此,我选择只使用 Laravel来建立 API 接口。 而当我需要建立 API 接口 的时候,Fractal是我首选的类库。 现在,情况发生了变化。
在 Laravel 5.5 中,我们有了 API 资源,对此,我真的是非常的兴奋。
在2小时前,Laravel 5.5 发布了,当时我正和朋友一起喝咖啡。当我在半小时前读到这条推的时候,脑子里的第一个想法就是用 API 资源来发布我的第一篇博客文章,我也确实这么做了。
Laravel 的 API 资源是基于 Fractal , 因此,我并没有花太多时间来了解如何使用它。 所以,让我们开始来了解它吧 ...
创建 Laravel 应用
用常用的命令行来创建 Laravel 应用
composer create-project laravel/laravel Laravel55Api
应用创建完成后,将 .env.example 重命名为 .env 并用以下命令生成 Laravel 密钥。
php artisan key:generate
启动服务
php artisan serve
很好,接下来是什么呢?
创建一个 Product 资源
API 资源是在 Laravel 中将你的模型以及模型集合转换为 JSON 的新特性。接下来让我们创建一个 Product 的资源。
php artisan make:resource Product
你可以在 app/Http/Resources 目录下看到你刚刚生成的 Product 资源
当然我们还需要 Product 的数据库迁移、模型和控制器。我们能用这个命令快速的创建这些。
php artisan make:model Product -mc
打开数据库迁移文件然后像这样修改 up 方法里面的内容:
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('price');
$table->timestamps();
});
}
注意这里的价格字段是整型的。 我拜托你们,永远不要用浮点型存储你的价格数据!
一定要用整型来存储!
现在用你的 Laravel 应用连接数据库并运行这个迁移以生成数据表。
本文不是 Laravel 一对一教学贴,所以我不会在连接数据库的问题上浪费你过多的时间。
接下来?
到目前为止,我们已经有模型,控制器,数据库迁移以及用以转换模型和模型集合为 JSON 的资源类。那么接下来呢?
在这之前,什么是资源类?我们在 resources 文件夹中创建的 Product 类又是什么?一个资源类表示了单个模型转换为 JSON 的结构。
结合上面所阐述的,让我们来打开 Product.php 资源类文件。
这里有一个 toArray 方法,这个就是在我们发送响应时返回需要转换为 JSON 的属性数组的方法。
我们来修改它,让我们可以有更好的点子。
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'price' => $this->price,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
现在表示我们能够获得有 id, name, price, created_at 和 updated_at 这些字段的响应。
如果我们在 toArray 方法中去掉 price 字段,返回的 JSON 中就不会有 price 。很酷不是吗?
使用 Product 资源
我们刚刚更改了 toArray 方法,让我们继续在我们的控制器中使用 product 资源。
product 控制器是看起来是这样的:
<?php
namespace App\Http\Controllers;
use App\Product;
use App\Http\Resources\Product as ProductResource;
class ProductController extends Controller
{
public function show ($id)
{
return new ProductResource(Product::find($id));
}
}
为了转换 product ,我们仅仅在 product 资源类中传递了一个 product。
让我们创建一个 show 方法的路由,看一看结果。
打开 api.php 文件,在中间件外部创建这个路由。
Route::get('/products/{id}', 'ProductController@show');
现在,手动的在你的 products 表里添加一个新的 product,然后访问http://127.0.0.1:8000/api/products/1看看一个简单的 product。
你应该得到这样的结果:

现在让我们来修改一点我们的资源,假如你不想公开你的 product 的 price,你要做的就是简单的从你的 toArray 方法删除它。一旦你从 toArray 方法删除了 price,你应该得到这样的结果,当然不包括 price:

就这些吗?
当然不是!因为 toArray 方法仅仅是一个方法,它意味着你可以包含额外的信息。
假如我们想要包括一个「test」信息,简单的改变你的 toArray 方法。
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'test' => 'This is just a test',
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
这是结果:

然而,非常要重的是,你希望返回的数据类型总是正确的。在第一个截图上看看 price,它返回的是一个 integer,但是通过 (int) $this->price,我们仍然要强制它是一个integer。
现在,看看 create_at 和 updated_at 时间戳。如果你想要的是返回一个实际时间戳的字符串?那么你可以像这个例子中,强制类型转换为字符串:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'test' => 'This is just a test',
'created_at' => (string)$this->created_at,
'updated_at' => (string)$this->updated_at,
];
}
现在的结果是这个:

结束语
这仅仅是使用 Laravel API 资源的一个小例子。
如果我继续写下去,这篇文章永远也写不完。
所以,这篇文章就写到这吧,正如你所知道的,我们还有更多话题需要讨论,比如分页、资源集合、关联以及数据包裹等。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
关于 LearnKu
高认可度评论:
表示价格一般用
decimal@TimJuly 在设计price字段的时候,只要考虑以最小的单位为1基准就可以了,比如如果price是人民币的话,就以1分钱为1,那么1块钱存在数据库就是100。
讲道理,这个比 Fractal 灵活简单太多了,Fractal 过度设计还不灵活,用了之后我都快神经分裂了
fractal的 transformers的 include功能以及param的解析 功能我觉得很酷 ,
include=comments:limit(5|1):order(created_at|desc)类似这样的处理. 如何在api resource中使用类似这样的功能,并且还不会带来n+1的问题呢我拜托你们,永远不要用浮点型存储你的价格数据!这句不太明白什么意思。为什么价格不能float呢?如果用decimal呢?表示价格一般用
decimal@zhouxiaoshuai3 价格计算会引起计算错误
@遗忘的影子 感觉主要是误差不可控。
@遗忘的影子 原来这样啊,谢谢了!
@zhouxiaoshuai3
@LOST
可以参考 http://justjavac.iteye.com/blog/1724438
不能以偏概全啊,整数存钱就是个大坑,如果非要这么干,一定要想清楚.
例如:现在有1块钱分给500个小朋友,请问每人得多少钱?
@KevinYang 我也是用的decimal。。。
@TimJuly decimal 啊 ,怎么存不是你说了算呢
@TimJuly 2000
微。哦,其实就是 2
厘。 你给我我也找不开那种。@TimJuly 在设计price字段的时候,只要考虑以最小的单位为1基准就可以了,比如如果price是人民币的话,就以1分钱为1,那么1块钱存在数据库就是100。
价格一般都存分吧,没有存元的。:earth_africa:
datetime 还可以这样转呢
(string)$this->updated_at@Max 稍微复杂一点的就废了,像一个玩具
@我是谁 但大多数应用追求的就是简单. 尤其是前后端分离 mvvm架构中, 后端只是提供一个数据接口而已.并不需要做太过复杂的处理
@JaiminWu 其实最好的解决方案应该是decimal 不然等你处理到类似ETH这种数字货币的时候你就头大了 18位的小数点 你难道要* 10000000000000000 然后再处理么 不太可能的
Resource 中 created_at 字段怎样返回 unix 格式呢?难道只能 strtotime( ) 转换吗?
我想知道 $this->name 什么的 在编辑器里面标红 有什么办法解决的么?
已解决:
使用 下面的方式 就不会出现IDE飘红的问题了.
YII2是重写当前models的fileds方法,无需关注API,通过AR返回的数据会统一处理,代码如下:
我怎么觉得YII2更优雅呢:smile: ,restful api 也支持格式化响应,速率限制,版本化等等。
我来啦,哈哈。帮大家补充个更详细的教程。 《用 Laravel 构建 RESTful API 教程》 kalasearch.cn/community/tutorials/...
觉得有用点个赞哈。
分页集合
index和明细show返回需要的字段不一样,怎么处理?@Vicoo 建两个resource,一个用于列表,一个用于具体明细