Laravel翻到第二頁篩選消失:分頁連結保留限定query

商品清單選了coffee分類,第一頁正確,按第二頁卻變成所有商品。先把滑鼠移到分頁連結,看看網址是否只剩 ?page=2。後端第一個請求有讀分類,不代表Paginator產生下一頁時會自動保留那份篩選。

要修的是連結攜帶的條件,以及下一個請求如何使用條件。Laravel的 appends()可以把指定query加入分頁URL;若只需要category與q,保留這兩個即可,不必把所有目前請求參數都複製出去。

第一頁的查詢條件不會留在下一個請求

假設第一頁網址是 /items?category=coffee&q=C%2B%2B,控制器讀取category與q後查到清單。頁面結束後,下一次按分頁會發出新請求;如果href沒有這些query,控制器也就拿不到原條件。

這不是資料庫忽然忘記篩選,而是URL契約沒有完整傳遞。查修時先看第一頁的實際網址與第二頁href,再看控制器讀哪幾個query。不要只在控制器固定加coffee,讓第一個問題暫時消失,卻讓使用者無法切其他分類。

$items = Item::query()
    // 這裡沿用你的已驗證篩選條件。
    ->paginate(12);
$items->appends(['category' => 'coffee']);

這段示意連結追加的位置。真實查詢仍要把category用在資料查詢中;只加到URL卻沒使用它,不會讓下一頁自動篩對資料。分頁清單與連結要共用同一份已核對的條件。

明確保留需要的query

如果這個入口允許category與q,可先從query資料選出這兩個key,再交給Paginator。這讓回傳URL與頁面的篩選契約一致,也避免把無關參數一起帶到每一個連結。

$filters = collect($request->query())
    ->only(['category', 'q'])
    ->all();
$items->appends($filters);

選key不是驗證值。category是否存在、q是否符合長度與格式、是否有權讀取對應資料,仍要在你的請求與查詢流程中處理。本文只示意保留篩選的連結機制,不把任意query當作可信資料庫條件。

Laravel也提供 withQueryString(),方便保留目前請求的全部query。若頁面設計確實需要全部參數,這是可用選項;若只想保留兩個篩選,明確appends通常更容易知道每個連結帶了什麼。不要為了少寫一行,把不相關的追蹤或內部測試參數都長期附在分頁上。

用URL比較,不必先接完整商品資料

可以在自有Laravel測試專案建立一個LengthAwarePaginator,總數6、每頁2、目前第1頁。資料只放合成ID,專門觀察第二頁URL。

use Illuminate\Pagination\LengthAwarePaginator;
$p = new LengthAwarePaginator(
    [['id' => 1], ['id' => 2]],
    6, 2, 1,
    ['path' => 'https://example.test/items']
);
dump($p->url(2));
$p->appends(['category' => 'coffee', 'q' => 'C++ beans']);
dump($p->url(2));

Laravel13.34.0的本機案例中,第一個URL是 /items?page=2;追加後包含 category=coffee&q=C%2B%2B%20beans&page=2。再解析query,得到category coffee、q C++ beans、page2,說明加號與空白沒有被手動拼接弄壞。

這個例子核的是Paginator的URL,不是商品查詢或真頁面呈現。接回網站時,再按真正的第二頁連結,確認它發出的query與第一頁一致,結果確實仍屬同一個分類與搜尋條件。兩層都核,才能完整回答「翻頁條件是否保持」。

別手動拼出含加號的搜尋網址

搜尋字串可能含空白、加號與其他符號。直接把文字接在 &q=後面,容易產生不同解讀;例如原本的C++可能被當成空白。讓框架的query生成方法編碼參數,會比自己零散替換字元更清楚。

也不要把已經編碼的字串再當成原值交給appends,造成重複編碼。這個範例送進去的是原字串C++ beans,生成器才輸出相應URL。若你看到網址裡的百分比編碼不符合預期,先核資料在哪一層已被編碼,而不是再加一輪encode。

URL中的文字與實際查詢也要分開:使用者讀到C++,後端查詢取得的應是原字串,不是 %2B字面文字。Network、控制器輸入與Paginator輸出各看一眼,通常就能定位是哪一步改變了值。

切換篩選時,回到合適頁碼

保留query解決了翻頁丟條件,卻不代表所有舊頁碼都適合新篩選。使用者在第8頁改到只有一頁的分類,若表單又把page8帶過去,就可能得到空結果。切換篩選通常應把頁碼重設到第一頁,再由新清單的分頁連結繼續。

這是前端篩選提交與URL設計的工作,不能只在所有連結加appends就算完成。確認提交篩選時沒有保留不適用的page,再測新條件第一頁與第二頁。清單沒有資料,也應讓使用者知道目前條件與結果,不必自動把條件清掉來掩蓋空頁。

如果同一頁有兩組Paginator,Laravel支援替各組設定不同pageName。這時保留query要核對各組頁碼是否互相影響。本文只有一組預設page,不把單組範例推成所有多列表頁面都能照抄。

排序與篩選是另外的穩定條件

第二頁有保留category,仍可能因查詢沒有穩定排序而出現重複或跳項。這不是appends的責任;它只保留URL條件,不安排資料庫排序。查詢需要明確排序與必要的唯一鍵,以符合你的分頁資料要求。

若排序也讓使用者選擇,可以把允許的sort值一同納入篩選契約,先驗證,再保留到連結。不要把任意sort字串直接變成SQL欄位名或方向。每個可保留key都要有具體用途,讓頁面、查詢與URL一起使用同一份已整理資料。

最後從真頁連結走一次

在測試站先選分類,查看第二頁href,按下後核網址與結果;再換另一個分類,確認頁碼回到合適起點。搜尋含加號的文字也走一次,檢查它是否仍按原字串使用。這些操作能涵蓋連結、請求與結果,而不只是Tinker中的一個字串。

修正時只調整這份清單的分頁追加,不全站替換網址,也不改其他查詢功能。把需要保留的key寫清楚,往後新增篩選時就知道要同步更新哪個入口,不必再次猜為什麼第二頁看起來完全不同。

如果篩選欄位在表單裡,GET提交可以讓使用者看到目前條件,也方便分享相同清單。表單的欄位名稱必須與控制器讀取的query一致;前端叫cat、後端讀category,就算分頁保留了cat,也不會得到預期的篩選。這類命名差異應先修契約,不要在多處各自猜別名。

每頁顯示數量若可選,也要確認它納入同一份已驗證參數。使用者選每頁二十筆,第二頁卻回到預設十二筆,會改變項目範圍;這仍是請求條件沒有完整傳遞的問題。將必要參數集中整理後,同時用於查詢與appends,能減少兩邊逐漸不同步。

排除無關query不等於從資料列移除欄位。本文的only選的是網址參數,並沒有改變商品資料或權限。回傳哪些欄位、分頁結果能被誰看,應在自己的資料層處理,別把URL整理當成安全篩選已完成。

參考資料

原文鏈接:https://wntheme.com/laravel-pagination-appends-filter-query/,轉載請註明出處。
0

評論0

顯示驗證碼
沒有帳號?註冊  忘記密碼?