昨天探索了在 Laravel 裡,建立 Resources 和 Resource Collections,是輸入 php artisan make:resource
指令。當我們使用 Resource Collections 的時候,系統會自動從單一 Resources 那裡抓取資料,不需用我們手動設定。如果你想保留資料的原始 key,只要在 Resource Collections 裡加上 public $preserveKeys = true;
就好了。另外,如果你想要輸出的格式不一樣,還可以給資源集合指定其他的 Resources 類別,這樣就能自定義回應內容,讓 API 更符合我們的需求。
今天要學習的是在 Laravel 裡,如何方便的處理關聯資料,我們可以在 Resources 中定義要顯示的關聯資料,然後用 whenLoaded
和 whenCounted
方法來決定是否顯示這些資料。如果想根據條件來顯示或隱藏某些屬性,可以使用 when
和 mergeWhen
,這樣就能避免重複邏輯。
在開始之前,建議先了解基本概念。資源的主要作用是將模型轉換成可以使用的數據格式(陣列),這樣可以通過應用程式的路由或控制器返回。
每個資源都有一個 toArray
方法,這個方法會把模型的屬性轉換為一個陣列。
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
接下來,可以從路由或控制器中回傳
use App\Http\Resources\UserResource;
use App\Models\User;
Route::get('/user/{id}', function (string $id) {
return new UserResource(User::findOrFail($id));
});
我們可以使用 toArray
方法在返回的數據中包含相關 Resources
use App\Http\Resources\PostResource;
use Illuminate\Http\Request;
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
// 加入使用者的文章
'posts' => PostResource::collection($this->posts),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
資源的外層會被包裝在一個 data
陣列中。這樣返回的數據格式看起來像這樣:
{
"data": [
{
"id": 1,
"name": "Eladio Schroeder Sr.",
"email": "therese28@example.com"
},
{
"id": 2,
"name": "Liliana Mayert",
"email": "evandervort@example.com"
}
]
}
如果你不想要這個 data
包裝,我們可以在 AppServiceProvider
使用 withoutWrapping
方法。
可以使用 when
方法,來根據條件決定是否要在回應中加入某個屬性。
像是,只有在客人是VIP的時候,菜單上才會顯示那些獨特的菜色。
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
// 當使用者是 Admin 時,才會在回應中包含 'secret' => 'secret-value'
'secret' => $this->when($request->user()->isAdmin(), 'secret-value'),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
單一屬性
whenHas: 用於檢查某個屬性是否存在於模型中,若存在則返回該屬性。
'name' => $this->whenHas('name')
whenNotNull: 用於檢查某個屬性是否不為 null
,若不為 null
則返回該屬性。
'name' => $this->whenNotNull($this->name)
Merging Conditional Attributes
當我們有多個屬性的時候,不需要使用兩次 when
方法,我們可以使用 mergeWhen
方法來簡化這一點
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
// 在這裡我們使用陣列方式
$this->mergeWhen($request->user()->isAdmin(), [
'first-secret' => 'value',
'second-secret' => 'value',
]),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
在 Laravel 中,除了可以有選擇性地加載屬性之外,還可以根據模型裡面是否已經有加載的關係來決定是否要把這些關係放進回傳資料裡。這樣,控制器就能決定哪些關係要加載,而資源只在真的有這些資料的時候才顯示出來。就能更輕鬆地避免資源中出現 「N+1」 查詢問題。
whenLoaded
方法
這個方法可以用來條件性地加載關係。只有在關係已經加載的情況下,相關的數據才會包含在響應中。
// 如果 posts 關係沒有加載,則 posts 鍵會從響應中移除。
'posts' => PostResource::collection($this->whenLoaded('posts')),
whenCounted
方法
此方法用於根據關係的計數是否已加載,條件性地包括關係的計數。
// 如果 posts 的計數沒有加載,則 posts_count 鍵也會從響應中移除。
'posts_count' => $this->whenCounted('posts'),
了解 Laravel 的 Resources 和 Resource Collections 能讓我們的 API 變得更聰明、更有效率。可以多用條件屬性和關聯資料,這樣不僅能節省資源,還能讓我們的應用表現更佳唷!
踏著身心靈的塔羅腳步,轉向技術與邏輯的工程師之路,就藉由塔羅日抽來紀錄今日的學習與生活吧!
寶劍騎士:今天在寫的時候,有比前幾天多了些流場,可能是快要完賽的興奮感嗎?XD
手 て ぇ出 だ すならしまいまでやれ
只要決定開始,就用心去做吧,直到最後一刻!
— 神隱少女