Zi 字媒體
2017-07-25T20:27:27+00:00
Laravel Eloquent API Resources
PHP:7.2
Laravel:5.8
當我們利用 Laravel 在設計 API 時,通常都會透過 Eloquent Model 取得資料,並將資料轉換成 JSON 格式輸出。
比如在 Controller 中就會像這樣回傳:
return response()->json([
'status' => 200,
'data' => [
'meta' => SongModel::find($songID)
]
]);
然而,這裡隱藏著幾個缺點:
重複的格式定義: 類似的 endpoint 可能都有類似的格式定義,可是卻散落在 Controller 各處。
format 不固定: 因為是直接將 model 輸出,如果資料表有更動的話,API 會直接受到衝擊。
為了因應以上問題,Laravel 在 5.5 之後提供了 API Resources 讓大家更方便的自訂輸出格式,建立 Model 與 Output 之間的橋樑。
Basic Usage
Artisan 提供了命令列可以直接建立 Resource。
php artisan make:resource SongResource
產生後便可以在 app\Http\Resources 看到 SongResource 檔案。
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class SongResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}
由於 Resource 是繼承自 JsonResource,因此輸出就會是 JSON 的格式內容,而我們要做的就是修改 toArray() method 當中的格式:
public function toArray($request)
{
return [
'song_id' => $this->song_id,
'song_name' => $this->song_name,
'artist_id' => $this->artist_id,
'album_id' => $this->album_id,
];
}
接著只要在 Route 或 Controller 中直接輸出就可以了。
Controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Song;
use App\Http\Resources\SongResource;
class TestController extends Controller
{
public function test()
{
$model = SongModel::find($songID);
// 直接 return Resource
return new SongResource($model);
// 也可以自行設定 HTTP code
return (new SongResource($model))
->response()
->setStatusCode(200);
}
}
Route
use App\Song;
use App\Http\Resources\SongResource;
Route::get('/songs/{song}', function(Song $song) {
return new SongResource($song);
});
Output
{
"data":{
"song_id":301231008,
"song_name":"TEST",
"artist_id":3182,
"album_id":30497178,
}
}
Collection
接下來問題來了,很多時候我們會取得資源的清單,比如說 SongModel::all(),直接使用 Resource 會出現錯誤訊息,因為丟進去的是一個 Collection ,並不是物件本身。
此時需要搭配 Resource::collection() 使用,它會將 Collection 中的所有 Model 物件自動套用 Resource 的格式。
Controller Exmaple
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Song;
use App\Http\Resources\SongResource;
class TestController extends Controller
{
public function test()
{
$collection = SongModel::all();
return SongResource::collection($collection);
}
}
當然,也可以定義一些 Collection 的資料在 Resource 之中:
app/Http/Resources/SongResource
public function toArray($request)
{
return [
// 傳入 collection
'data' => $this->collection,
'count' => $this->collection->count()
];
}
Relationships
既然用到了 Eloquent Model,必定會用到一些 relation 去取得不同張表的資料;對於 Resource 來說,我們必須將這些資源都一一拆開,一來具備開發彈性、二來這些資源也可重複使用。
舉例來說,如果透過 SongModel 關聯出 Album 跟 Artist,可以設計成以下這樣:
app/Http/Resources/ArtistResource
public function toArray($request)
{
return [
'artist_id' => $this->artist_id,
'artist_name' => $this->artist_name
];
}
app/Http/Resources/AlbumResource
public function toArray($request)
{
return [
'album_id' => $this->album_id,
'album_name' => $this->album_name
];
}
app/Http/Resources/SongResource
public function toArray($request)
{
return [
'song_id' => $this->song_id,
'song_name' => $this->song_name,
'artist_id' => $this->artist_id,
'album_id' => $this->album_id,
'artist' => new Artist($this->artist),
'album' => new Album($this->album)
];
}
Controller Exmaple
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Song;
use App\Http\Resources\SongResource;
class TestController extends Controller
{
public function test()
{
$collection = SongModel::with(['album', 'artist'])->all();
return SongResource::collection($collection);
}
}
Categories: Laravel
Tags: LaravelPHP
分類
Android
AngularJS
API Blueprint
Chrome
Database
MySQL
DataStructure
Editor
Vim
Firefox
Git
Hadoop
Language
Go
Java
JavaScript
jQuery
jQueryChart
Node.js
Vue
PHP
Laravel
ZendFramework
Python
Mac
Network
Cisco
DLink
Juniper
Oauth
Server
Apache
Share
Unix
FreeBSD
Linux
WebDesign
Bootstrap
CSS
HTML
Wordpress
Search
搜尋:
寫了
5860316篇文章,獲得
23313次喜歡