從Laravel 5.5+開始,加入了API Resources這個(gè)概念。
我們先來看一下官網(wǎng)如何定義這個(gè)概念的:
When building an API, you may need a transformation layer that sits between your Eloquent models and the JSON responses that are actually returned to your application's users. Laravel's resource classes allow you to expressively and easily transform your models and model collections into JSON.
可能看完這個(gè)概念之后,你仍然有點(diǎn)不明白,畢竟這個(gè)定義說的有點(diǎn)含糊。
如果你熟悉使用API進(jìn)行輸出,構(gòu)架前后端分離的網(wǎng)絡(luò)應(yīng)用,那么你應(yīng)該會(huì)發(fā)現(xiàn),當(dāng)我們使用Eloquent從數(shù)據(jù)庫中取出數(shù)據(jù)后,如果想以JSON格式進(jìn)行輸出,那么我們可以使用->toJson()這個(gè)方法,這個(gè)方法可以直接將我們的model序列化(這個(gè)方法從Laravel 5.1+開始就可以使用了):
$user = App\User::find(1); return $user->toJson();
使用多了,我們會(huì)發(fā)現(xiàn),在model較為復(fù)雜,或者model中有很多我們API輸出可能用不到的字段的情況下,toJson()仍然會(huì)忠實(shí)地幫我們把這些字段序列化出來。
這個(gè)時(shí)候,我們會(huì)想,如何將model中的某些字段隱藏起來,不輸出到JSON中。另外一種情況,比如字段是password等一些敏感信息的時(shí)候,我們不希望JSON數(shù)據(jù)里包含這樣的敏感信息。
要解決這個(gè)問題,我們可以在model里定義$hidden或者$visible這兩個(gè)數(shù)組來進(jìn)行字段的隱藏或者顯示:
?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * 不希望在序列化中出現(xiàn)的字段放入該數(shù)組中 * * @var array */ protected $hidden = ['password', 'some', 'secret']; }
?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * 只有在以下數(shù)組中出現(xiàn)的字段會(huì)被序列化 * * @var array */ protected $visible = ['first_name', 'last_name']; }
那么你可能會(huì)想,我們已經(jīng)有了可以自動(dòng)序列化的方法,以及可以隱藏或者顯示指定字段的方法,這樣不就足夠了嗎?
現(xiàn)在我們來假設(shè)一個(gè)簡單的應(yīng)用場景。假設(shè)我們在輸出一個(gè)客戶列表,里面包含了客戶名字和送貨地址。我們使用Customer這個(gè)model定義客戶,使用ShippingAddress這個(gè)model進(jìn)行定義送貨地址。為了簡化場景,我們的客戶只有一個(gè)送貨地址,所以只會(huì)出現(xiàn)一一對(duì)應(yīng)的情況。
那么在ShippingAddress對(duì)應(yīng)的數(shù)據(jù)庫表shipping_addresses中,我們可能會(huì)有如下定義:
| id | country_id | province_id | city_id | address |
字段類型我就不贅述了,其中country_id、province_id以及city_id這三個(gè)外鍵分別對(duì)應(yīng)了國家、省份以及城市表中的id。
而Customer對(duì)應(yīng)的customers表中,會(huì)有shipping_address_id這個(gè)外鍵指向shipping_addresses表中的id。
那么我們要輸出顧客和送貨地址,我們需要先在model中定義好relationship:
?php namespace App; use Illuminate\Database\Eloquent\Model; class Customer extends Model { public function shippingAddress() { return $this->belongsTo(ShippingAddress::class); } }
?php namespace App; use Illuminate\Database\Eloquent\Model; class ShippingAddress extends Model { public function country() { return $this->belongsTo(Country::class); } public function province() { return $this->belongsTo(Province::class); } public function city() { return $this->belongsTo(City::class); } }
在我們的控制器中,我們拉取出所有客戶:
?php namespace App\Http\Controllers; use App\Customer; use App\Http\Controllers\Controller; class CustomerController extends Controller { /** * Simple function to fetch all customers with their shipping addresses * * @return String */ public function index() { $customers = Customer::with(['shippingAddress', 'shippingAddress.country', 'shippingAddress.province', 'shippingAddress.city'])->get(); //這里可以直接返回Eloquent Collections或Objects,toJson()將自動(dòng)被調(diào)用 return $customers; } }
那么輸出的JSON將會(huì)包含了多個(gè)層級(jí)的關(guān)系,那么在我們前端調(diào)用的時(shí)候,將會(huì)非常麻煩,因?yàn)槲覀冃枰粚右粚觿冮_Object關(guān)系。
但是如果你熟悉Laravel,你可能會(huì)說,慢著!這個(gè)情況我可以用accessor不就完事兒了嗎?
是的,我們確實(shí)可以使用accessor來簡化我們的數(shù)據(jù)層級(jí):
/** * Get the customer's full shipping address * * @return string */ public function getFullShippingAddressAttribute() { return "{$this->shippingAddress->country->name} {$this->shippingAddress->province->name} {$this->shippingAddress->city->name} {$this->shippingAddress->address}"; }
但是我們還需要一步操作。由于customers這張表本身沒有full_shipping_address這個(gè)字段,要使我們的JSON輸出包含full_shipping_address,我們需要添加$appends數(shù)組:
?php namespace App; use Illuminate\Database\Eloquent\Model; class Customer extends Model { /** * The accessors to append to the model's array form. * * @var array */ protected $appends = ['full_shipping_address']; }
對(duì)于每一個(gè)我們想自定義的JSON字段,我們都需要進(jìn)行上面兩部的操作。這樣一來其實(shí)非常麻煩,并且不利于代碼的維護(hù),因?yàn)檫@會(huì)讓原本簡潔的model顯得很復(fù)雜。
基于以上原因,我們需要一個(gè)中間層,在我們輸出model成為JSON的時(shí)候,可以進(jìn)行一次信息的過濾及加工。
那么還是使用我們上面的應(yīng)用場景。要輸出自定義的字段再簡單不過了。我們不需要在model里定義各種accessor,也不需要使用黑白名單過濾字段,只需要新建一個(gè)Resource類:
$ php artisan make:resource Customer
然后我們可以看到,在app/Http文件夾下,多出了一個(gè)名為Resources文件夾下,其中含有一個(gè)名為Customer.php的文件:
?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; class Customer extends JsonResource { /** * Transform the resource into an array. * * @param \Illuminate\Http\Request $request * @return array */ public function toArray($request) { return parent::toArray($request); } }
這里我們看到其中有且僅有一個(gè)名為toArray的方法。這就是我們要自定字段的地方:
public function toArray($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表中的字段。
接著,我們只需要簡單修改一下我們的控制器:
?php namespace App\Http\Controllers; use App\Customer; use App\Http\Resources\Customer as CustomerResource; use App\Http\Controllers\Controller; class CustomerController extends Controller { /** * Simple function to fetch all customers with their shipping addresses * * @return String */ public function index() { $customers = Customer::with(['shippingAddress', 'shippingAddress.country', 'shippingAddress.province', 'shippingAddress.city'])->get(); //這里我們使用了新的Resource類 return CustomerResource::collection($customers); } }
這樣就OK了!我們輸出的JSON數(shù)據(jù)中,將會(huì)僅僅含有以上兩個(gè)字段,即fullName和fullShippingAddress,非常干凈,并且前端直接可用,不需要二次再加工。
唯一需要注意的是,這里由于我們拉取了多個(gè)Customer,所以我們用了每個(gè)Resource類都自帶有的collection方法,將一個(gè)Collection中的所有對(duì)象都進(jìn)行處理。而若要處理單個(gè)對(duì)象,我們需要使用以下代碼:
public function show($id) { $customer = Customer::findOrFail($id); return new CustomerResource($customer); }
要了解更多關(guān)于API Resources的詳情,請(qǐng)戳官網(wǎng)文檔:
https://laravel.com/docs/5.7/eloquent-resources
本文主要講解了Laravel5.5+ 使用API Resources快速輸出自定義JSON方法詳解,更多關(guān)于Laravel框架的使用技巧請(qǐng)查看下面的相關(guān)鏈接
標(biāo)簽:黃山 唐山 河南 南通 武漢 平頂山 通遼 隴南
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Laravel5.5+ 使用API Resources快速輸出自定義JSON方法詳解》,本文關(guān)鍵詞 Laravel5.5+,使用,API,Resources,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。