iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0
Software Development

Laravel專案練習-寶可夢管理系統系列 第 20

Day20:寶可夢專案-寶可夢API開發-n+1問題

  • 分享至 

  • xImage
  •  

我在開發API的時候,對於如何拿到關聯資料返回數據我覺得有一些東西可以分享。

以下是我其中一個功能,他可以讓使用者查看目前所有新增的寶可夢。

public function index()
    {
        // 透過JWT取得當前登入的用戶
        $user = auth()->user();

        $pokemons = $user->pokemons()->with(['user', 'ability', 'nature', 'race'])->get();
        return PokemonResource::collection($pokemons);
}

總之我是透過和寶可夢表的關連去拿到數據,而我這裡先透過with預加載的方式去解決n+1問題,也就是在我的resource裡面去取的關聯資料的時候,我不用每次要去取得一隻寶可夢的關聯資料就要取一次。

  • n+1問題

    我的理解就是:

    假設我們有一個**Author(作者)表格和一個Book(書籍)表格。每位Author都有多本Book**。

    現在,我們想從資料庫中查詢所有的作者和他們的書籍。

    如果使用簡單的查詢方法,可能會這麼做:

    1. 查詢所有的**Author**。
    2. 對於每位**Author,查詢其所寫的Book**。

    這樣的話,假如有10位**Author,我們就需要執行1次查詢Author的操作,再加上10次查詢Book**的操作,總共是11次查詢。

    • 那這樣會有什麼問題呢?

      假設今天資料庫是做全表搜尋(也就是從頭到尾掃過一遍去找你的資料),那你對他請求十次。他就要掃十次。

      但如果你今天一次說我要找作者id 1-10的書籍
      那他可能只要全表搜尋一次這十個作者的書籍然後一次返回就好,效率上就會有差。

但現在有一個問題是,我的pokemon和skills之間是沒有建立關聯的,所以我沒有辦法做愈加載的動作,於是我想了幾個方案:

1.先在進入到resource之前把skill資料都取得後再當作參數傳入resource,但後來我是沒想到要如何把這個東西傳入到resource然後再一一對應到每隻寶可夢所以沒有走這條路。

  • 2.使用cache:

    概念上就是,我先去一次把所有技能名稱及id取出來,然後放到cache裡,接者在和沒次循環的寶可夢的技能id陣列去比對,就可以找到對應的技能名稱了。

    此方法好處是,我今天只需要在第一次對資料庫做一次搜尋,而之後我cache的某個key裡面有值他就不會再去對資料庫做搜索。

    程式碼如下:

$minutes = 60; // 設定 cache 60 分鐘後過期
        $allSkills = Cache::remember('all_skills', $minutes, function () {
            return Skill::all();
        });
        $allSkillsArray = $allSkills->pluck('name', 'id')->toArray();

        // 取得 $this->skills 中指定的技能名稱
        $selectedSkillNames = array_intersect_key($allSkillsArray, array_flip($this->skills));
        // 如果需要,將其重新整理為索引陣列
        $selectedSkillNames = array_values($selectedSkillNames);

小結語

其實這種方法我也不確定是不是一個最優解,但就是目前能想到的一個方法,就分享一下。


上一篇
Day19 寶可夢專案-寶可夢資料庫的建立-排程設定
下一篇
Day21:寶可夢專案-寶可夢API開發-Form Request
系列文
Laravel專案練習-寶可夢管理系統30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言