iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0

今天要來講第三種 Collection:Resource Collection

如果畫成流程圖的話,三種 Collection 分別在這三個位置(簡略的畫一下我初步的理解)

https://ithelp.ithome.com.tw/upload/images/20231004/201628939aUaunWBw9.png


API Resource

前面有講過 API 與 RESTful API 的概念,後端工程師的工作就是建立這些 API (按鈕),讓前端工程師或其他使用者在需要時,按下特定按鈕可以得到某些特定資料。

所以 API 回傳的資料,就需要經過後端工程師的設計、處理,有些資料可能不適合傳遞給前端、有些資料為了符合前端需求需要進一步處理等等,就會在 API Resource 裡面撰寫。

備註:如果是純後端專案(前後端分離下),只需要回傳資料內容上圖,透過 API Resource 功能將這些資料加以整理。前後端不分離的狀況下,直接回傳變數帶入畫面渲染。
這裡故意比較兩種結果,通常專案不會既前後端不分離、又前後分離。

先看一下前端需要什麼內容

https://ithelp.ithome.com.tw/upload/images/20231004/20162893PfG8lLCGLD.png


尚未使用 API Resource 的情況

這裡我撰寫了一個 api route,當請求 (http://localhost:8000/api/group/34) 傳入專案時,會進到 GroupController,Controller 直接回傳 id=34 的團購資料。

Routes/api.php

Route::apiResource('/group', GroupController::class)
    ->only('show');

GroupController.php

public function show(Group $group)
{
    return $group;
}

https://ithelp.ithome.com.tw/upload/images/20231004/20162893PXYr3pvH5G.png

比對前端需求,在這支 API 需要收到:團購名稱、團購圖片、結單金額、結單日期、是否允許使用者新增購買品項、訂單狀態等資訊,顯然剛剛打 API 回傳的結果需要進一步處理,包含:

  • image_path 需要轉換為網址、
  • created_at 和 updated_at 不用顯示、
  • close_date 如果是 null 要顯示為” —- ”

這些需求後端工程師要跟前端進一步溝通,哪些欄位是不是真的不用顯示、如果是 null 要回傳什麼、前端處理還是後端處理(前端也可處理)。

這裡的範例可能沒有太大的感受,但如果回傳資料一多,十幾二十個欄位時就會很有感覺!


使用 API Resource

建立 GroupResource

使用 php artisan 指令建立

https://ithelp.ithome.com.tw/upload/images/20231004/20162893Gyl4kBkUuk.png

app/Http/Resources/GroupResource.php

class GroupResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id'                   => $this->id,
            'group_name'           => $this->group_name,
            "organizer_id"         => $this->organizer_id,
            "close_price"          => isset($this->close_price) ? $this->close_price : "---",
            "close_date"           => isset($this->close_date) ? $this->close_date : "---",
            "allow_insert_product" => $this->allow_insert_product,
            "status"               => $this->status,
            "image_path"           => Storage::url($this->image_path),
            'products'             => $this->whenLoaded('products'),
        ];
    }
}

GroupController

public function show(Group $group)
{
    return GroupResource::make($group);
				// 引入 GroupResource
}

回傳結果

搭啦~按照希望的方式回傳囉!

https://ithelp.ithome.com.tw/upload/images/20231004/20162893byxdcTCYXP.png


Resource Collection

上面處理的是單筆資料,像 index() 功能要一次回傳多筆資料,就需要 Collection 協助

public function index() {
		$groups = Group::where('status', Group::STATUS_OPEN)
				->orderBy('close_date', 'ASC')
        ->orderBy('updated_at', 'DESC')
        ->orderBy('id', 'ASC')
        ->paginate(
            $perPage = 4, $columns = ['*'], $pageName = 'groups'
        );

    return $groups;
}

https://ithelp.ithome.com.tw/upload/images/20231004/20162893CIsWTZMGG9.png

(1) 使用 GroupResource::collection() 方法

GroupController

public function index()
{
		$groups = Group::where('status', Group::STATUS_OPEN)
				->orderBy('close_date', 'ASC')
				->orderBy('updated_at', 'DESC')
				->orderBy('id', 'ASC')
				->paginate(
						$perPage = 4, $columns = ['*'], $pageName = 'groups'
				);
		return GroupResource::collection($groups); // 使用 collection() 方法
}

輸出結果

{
    "data": [
        {
            "id": 36,
            "group_name": "丸龜製麵",
            "organizer_id": 1,
            "close_price": "9000.00",
            "close_date": "---",
            "allow_insert_product": null,
            "status": 1,
            "image_path": "http://localhost:8000/storage/public/groups/FEnBqBkhfeXUPFuwh6MOBpUAgkS2ZAvGkMQduqTN.png"
        },
        {
            "id": 34,
            "group_name": "丸龜製麵",
            "organizer_id": 1,
            "close_price": "2500.00",
            "close_date": "---",
            "allow_insert_product": null,
            "status": 1,
            "image_path": "http://localhost:8000/storage/public/groups/lK9cFTKAI7nYFyWnsF9nkxlMxCpHmek4LkXChN4e.png"
        },
        {
            "id": 2,
            "group_name": "肯德基",
            "organizer_id": 2,
            "close_price": "1000.00",
            "close_date": "---",
            "allow_insert_product": 1,
            "status": 1,
            "image_path": "http://localhost:8000/storage/https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTPhDv3ncnk3W1CSDwU498WBrMmosbIChVBVA&usqp=CAU"
        },
        {
            "id": 3,
            "group_name": "睛水股份有限公司",
            "organizer_id": 3,
            "close_price": "1000.00",
            "close_date": "---",
            "allow_insert_product": 1,
            "status": 1,
            "image_path": "http://localhost:8000/storage/揪團囉.png"
        }
    ],
    "links": {
        "first": "http://localhost:8000/api/group?groups=1",
        "last": "http://localhost:8000/api/group?groups=4",
        "prev": null,
        "next": "http://localhost:8000/api/group?groups=2"
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 4,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://localhost:8000/api/group?groups=1",
                "label": "1",
                "active": true
            },
            {
                "url": "http://localhost:8000/api/group?groups=2",
                "label": "2",
                "active": false
            },
            {
                "url": "http://localhost:8000/api/group?groups=3",
                "label": "3",
                "active": false
            },
            {
                "url": "http://localhost:8000/api/group?groups=4",
                "label": "4",
                "active": false
            },
            {
                "url": "http://localhost:8000/api/group?groups=2",
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://localhost:8000/api/group",
        "per_page": 4,
        "to": 4,
        "total": 16
    }
}

(2) 使用 GroupCollection::make( ) 方法

生成 collection 檔案

https://ithelp.ithome.com.tw/upload/images/20231004/201628937URoHQs8Xm.png

GroupController

public function index()
{
		$groups = Group::where('status', Group::STATUS_OPEN)
				->orderBy('close_date', 'ASC')
				->orderBy('updated_at', 'DESC')
				->orderBy('id', 'ASC')
				->paginate(
						$perPage = 4, $columns = ['*'], $pageName = 'groups'
				);
		return GroupCollection::make($groups);
}

GroupCollection

暫時不用任何操作 (對!你沒看錯)

回傳結果說明

回傳結果會跟上面 GroupResource::collection() 方法相同,不過兩個方法底層是不太相同的,有興趣可以自己追一下!

(3) 利用 GroupCollection 做更多設定

上面使用 GroupCollection 並沒有進一步撰寫不同邏輯,所以回傳結果相同,這次來試試看撰寫不同邏輯

app/Http/Resources/GroupCollection.php

class GroupCollection extends ResourceCollection
{
    public function toArray(Request $request): array
    {
        return [
            'groups'  => $this->collection,
            'message' => "Wonderful!"
        ];
    }
}

回傳結果

{
    "data": {
        "groups": [  // 我希望 團購資料被塞入的陣列名稱 
            {
                "id": 36,
                "group_name": "丸龜製麵",
                "organizer_id": 1,
                "close_price": "9000.00",
                "close_date": "---",
                "allow_insert_product": null,
                "status": 1,
                "image_path": "http://localhost:8000/storage/public/groups/FEnBqBkhfeXUPFuwh6MOBpUAgkS2ZAvGkMQduqTN.png"
            },
            {
                "id": 34,
                "group_name": "丸龜製麵",
                "organizer_id": 1,
                "close_price": "2500.00",
                "close_date": "---",
                "allow_insert_product": null,
                "status": 1,
                "image_path": "http://localhost:8000/storage/public/groups/lK9cFTKAI7nYFyWnsF9nkxlMxCpHmek4LkXChN4e.png"
            },
            {
                "id": 2,
                "group_name": "肯德基",
                "organizer_id": 2,
                "close_price": "1000.00",
                "close_date": "---",
                "allow_insert_product": 1,
                "status": 1,
                "image_path": "http://localhost:8000/storage/https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTPhDv3ncnk3W1CSDwU498WBrMmosbIChVBVA&usqp=CAU"
            },
            {
                "id": 3,
                "group_name": "睛水股份有限公司",
                "organizer_id": 3,
                "close_price": "1000.00",
                "close_date": "---",
                "allow_insert_product": 1,
                "status": 1,
                "image_path": "http://localhost:8000/storage/揪團囉.png"
            }
        ],
        "message": "Wonderful!"  // 我塞入的訊息
    },
    "links": {
        "first": "http://localhost:8000/api/group?groups=1",
        "last": "http://localhost:8000/api/group?groups=4",
        "prev": null,
        "next": "http://localhost:8000/api/group?groups=2"
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 4,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            // 略 ...
        ],
        "path": "http://localhost:8000/api/group",
        "per_page": 4,
        "to": 4,
        "total": 16
    }
}

後記

今天的第三種 collection 跟前面兩種比較不一樣,但都是對多筆資料的處理。

看完這三篇,下次不會再被 Laravel Collection 這個字混淆啦~


上一篇
等等,那 Eloquent Collection 是什麼?
下一篇
Laravel filesystem / 專案筆記:團購圖片上傳功能
系列文
Laravel 後端菜鳥可以知道的流程概念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言