Laravel5.5+ 使用API Resources快速输出自定义JSON方法详解
从Laravel5.5+开始,加入了APIResources这个概念。
我们先来看一下官网如何定义这个概念的:
WhenbuildinganAPI,youmayneedatransformationlayerthatsitsbetweenyourEloquentmodelsandtheJSONresponsesthatareactuallyreturnedtoyourapplication'susers.Laravel'sresourceclassesallowyoutoexpressivelyandeasilytransformyourmodelsandmodelcollectionsintoJSON.
可能看完这个概念之后,你仍然有点不明白,毕竟这个定义说的有点含糊。
如果你熟悉使用API进行输出,构架前后端分离的网络应用,那么你应该会发现,当我们使用Eloquent从数据库中取出数据后,如果想以JSON格式进行输出,那么我们可以使用->toJson()这个方法,这个方法可以直接将我们的model序列化(这个方法从Laravel5.1+开始就可以使用了):
$user=App\User::find(1); return$user->toJson();
使用多了,我们会发现,在model较为复杂,或者model中有很多我们API输出可能用不到的字段的情况下,toJson()仍然会忠实地帮我们把这些字段序列化出来。
这个时候,我们会想,如何将model中的某些字段隐藏起来,不输出到JSON中。另外一种情况,比如字段是password等一些敏感信息的时候,我们不希望JSON数据里包含这样的敏感信息。
要解决这个问题,我们可以在model里定义$hidden或者$visible这两个数组来进行字段的隐藏或者显示:
那么你可能会想,我们已经有了可以自动序列化的方法,以及可以隐藏或者显示指定字段的方法,这样不就足够了吗?
现在我们来假设一个简单的应用场景。假设我们在输出一个客户列表,里面包含了客户名字和送货地址。我们使用Customer这个model定义客户,使用ShippingAddress这个model进行定义送货地址。为了简化场景,我们的客户只有一个送货地址,所以只会出现一一对应的情况。
那么在ShippingAddress对应的数据库表shipping_addresses中,我们可能会有如下定义:
|id|country_id|province_id|city_id|address|
字段类型我就不赘述了,其中country_id、province_id以及city_id这三个外键分别对应了国家、省份以及城市表中的id。
而Customer对应的customers表中,会有shipping_address_id这个外键指向shipping_addresses表中的id。
那么我们要输出顾客和送货地址,我们需要先在model中定义好relationship:
belongsTo(ShippingAddress::class); } }belongsTo(Country::class); } publicfunctionprovince() { return$this->belongsTo(Province::class); } publicfunctioncity() { return$this->belongsTo(City::class); } }在我们的控制器中,我们拉取出所有客户:
get(); //这里可以直接返回EloquentCollections或Objects,toJson()将自动被调用 return$customers; } }那么输出的JSON将会包含了多个层级的关系,那么在我们前端调用的时候,将会非常麻烦,因为我们需要一层一层剥开Object关系。
但是如果你熟悉Laravel,你可能会说,慢着!这个情况我可以用accessor不就完事儿了吗?
是的,我们确实可以使用accessor来简化我们的数据层级:
/** *Getthecustomer'sfullshippingaddress * *@returnstring */ publicfunctiongetFullShippingAddressAttribute() { return"{$this->shippingAddress->country->name}{$this->shippingAddress->province->name}{$this->shippingAddress->city->name}{$this->shippingAddress->address}"; }但是我们还需要一步操作。由于customers这张表本身没有full_shipping_address这个字段,要使我们的JSON输出包含full_shipping_address,我们需要添加$appends数组:
对于每一个我们想自定义的JSON字段,我们都需要进行上面两部的操作。这样一来其实非常麻烦,并且不利于代码的维护,因为这会让原本简洁的model显得很复杂。
基于以上原因,我们需要一个中间层,在我们输出model成为JSON的时候,可以进行一次信息的过滤及加工。
那么还是使用我们上面的应用场景。要输出自定义的字段再简单不过了。我们不需要在model里定义各种accessor,也不需要使用黑白名单过滤字段,只需要新建一个Resource类:
$phpartisanmake:resourceCustomer然后我们可以看到,在app/Http文件夹下,多出了一个名为Resources文件夹下,其中含有一个名为Customer.php的文件:
这里我们看到其中有且仅有一个名为toArray的方法。这就是我们要自定字段的地方:
publicfunctiontoArray($request) { return[ 'fullName'=>$this->first_name.$this->last_name, 'fullShippingAddress'=>$this->shippingAddress->country->name.$this->shippingAddress->province->name.$this->shippingAddress->city->name.$this->shippingAddress->address, ]; }注意到,无论是fullName还是fullShippingAddress,都是不存在于customers表中的字段。
接着,我们只需要简单修改一下我们的控制器:
get(); //这里我们使用了新的Resource类 returnCustomerResource::collection($customers); } }这样就OK了!我们输出的JSON数据中,将会仅仅含有以上两个字段,即fullName和fullShippingAddress,非常干净,并且前端直接可用,不需要二次再加工。
唯一需要注意的是,这里由于我们拉取了多个Customer,所以我们用了每个Resource类都自带有的collection方法,将一个Collection中的所有对象都进行处理。而若要处理单个对象,我们需要使用以下代码:
publicfunctionshow($id) { $customer=Customer::findOrFail($id); returnnewCustomerResource($customer); }要了解更多关于APIResources的详情,请戳官网文档:
https://laravel.com/docs/5.7/eloquent-resources
本文主要讲解了Laravel5.5+使用APIResources快速输出自定义JSON方法详解,更多关于Laravel框架的使用技巧请查看下面的相关链接