在 Laravel 應(yīng)用中,當(dāng)需要對(duì)主模型(如 User)及其關(guān)聯(lián)模型(如 Role、Department)進(jìn)行多條件模糊搜索時(shí),開發(fā)者常會(huì)遇到查詢結(jié)果不符合預(yù)期的問題。一個(gè)典型的場(chǎng)景是,用戶希望通過一個(gè)搜索詞,同時(shí)匹配用戶的姓名、郵箱,以及其所屬的角色或部門的標(biāo)題。
原始代碼嘗試通過 User::with([...])->search(...) 的方式來實(shí)現(xiàn),但這種方法存在根本性誤解:
$query = User::with([ 'roles' => function($query) use($searchValues) { return $query->where('title', 'LIKE','%'.$searchValues.'%'); }, 'departments' => function($query) use($searchValues) { return $query->where('title', 'LIKE','%'.$searchValues.'%'); } ]) ->search($searchValues) // 這里的 search scope 僅針對(duì) User 模型字段 ->orderBy($orderColumnName,$order) ->limit($request->length) ->get();
上述代碼中,with() 方法用于預(yù)加載關(guān)聯(lián)數(shù)據(jù),以便在獲取 User 模型實(shí)例后,能夠方便地訪問其 roles 和 departments。with() 中的閉包條件只是限制了預(yù)加載的關(guān)聯(lián)數(shù)據(jù),而不是用來過濾主模型(User)的。這意味著,即使某個(gè)用戶不滿足 roles 或 departments 的搜索條件,只要他滿足 User 自身的 search 條件,該用戶仍然會(huì)被查詢出來,只是其關(guān)聯(lián)的 roles 或 departments 可能為空或被過濾。這與我們期望的“通過關(guān)聯(lián)模型條件來篩選主模型”的目的背道而馳。
要解決上述問題,我們需要使用 whereHas 或 orWhereHas 方法。這些方法允許我們根據(jù)關(guān)聯(lián)模型的條件來過濾主模型。
在我們的多條件模糊搜索場(chǎng)景中,用戶可能希望匹配任意一個(gè)條件(用戶姓名、郵箱,或角色標(biāo)題,或部門標(biāo)題),因此 orWhereHas 是更合適的選擇。
為了實(shí)現(xiàn)既能搜索用戶自身屬性,又能搜索關(guān)聯(lián)模型屬性的復(fù)雜查詢,我們需要將 scopeSearch 與 orWhereHas 結(jié)合起來,并注意 orWhere 的邏輯分組。
首先,確保 User 模型中的 scopeSearch 方法正確定義,用于搜索用戶自身的 first_name, last_name, email:
// app/Models/User.php class User extends Authenticatable { // ... 其他屬性和方法 ... /** * 作用域:根據(jù)姓名和郵箱進(jìn)行模糊搜索。 */ public function scopeSearch($query, $searchValues) { // 注意:這里的 orWhere 是針對(duì) User 模型的字段 return $query->where('first_name', 'like', '%'.$searchValues.'%') ->orWhere('last_name', 'like', '%'.$searchValues.'%') ->orWhere('email', 'like', '%'.$searchValues.'%'); } // ... 關(guān)聯(lián)關(guān)系定義 ... public function departments() { return $this->belongsToMany(Department::class, 'users_departments', 'user_id', 'department_id'); } public function roles() { return $this->belongsToMany(Role::class, 'users_roles', 'user_id', 'role_id'); } }
接下來,構(gòu)建正確的 Eloquent 查詢:
use App\Models\User; use Illuminate\Http\Request; // 假設(shè) $request->searchValues, $request->orderColumnName, $request->order, $request->length 已定義 // 定義一個(gè)可復(fù)用的閉包,用于關(guān)聯(lián)模型的搜索條件 $searchClosure = function ($query) use ($searchValues) { return $query->where('title', 'LIKE', "%{$searchValues}%"); }; $users = User::with(['roles' => $searchClosure, 'departments' => $searchClosure]) ->where(function ($query) use ($searchValues, $searchClosure) { // 首先應(yīng)用 User 模型的自身搜索條件 $query->search($searchValues); // 然后使用 orWhereHas 引入關(guān)聯(lián)模型的搜索條件 // 注意:這里的 orWhereHas 會(huì)在外部的 where 閉包中與 scopeSearch 的條件進(jìn)行 OR 邏輯組合 $query->orWhereHas('roles', $searchClosure); $query->orWhereHas('departments', $searchClosure); }) ->orderBy($orderColumnName, $order) ->limit($request->length) ->get();
代碼解釋:
理解這兩者的核心區(qū)別至關(guān)重要:
with() (預(yù)加載):
whereHas() / orWhereHas() (條件過濾):
簡(jiǎn)而言之,with 是“獲取這些用戶,并把他們的角色和部門也帶上”,而 whereHas/orWhereHas 是“只獲取那些有特定角色或部門的用戶”。在復(fù)雜搜索中,兩者通常需要結(jié)合使用:先用 whereHas/orWhereHas 過濾出符合條件的主模型,再用 with 預(yù)加載這些主模型的關(guān)聯(lián)數(shù)據(jù)。
Laravel 官方文檔強(qiáng)烈建議在使用 orWhere 時(shí)進(jìn)行邏輯分組,以避免意外行為。例如:
// 推薦的做法 $query->where(function ($q) { $q->where('name', 'like', '%John%') ->orWhere('email', 'like', '%John%'); }); // 可能導(dǎo)致意外行為(尤其當(dāng)有全局作用域時(shí)) $query->where('status', 'active') ->orWhere('name', 'like', '%John%') ->orWhere('email', 'like', '%John%');
在我們的解決方案中,我們將所有的 search 和 orWhereHas 條件都包裹在一個(gè) where 閉包中,這正是遵循了 orWhere 邏輯分組的最佳實(shí)踐,確保了整個(gè)搜索邏輯的清晰性和正確性。
$usersQuery = User::with([...])->where(function ($query) { /* ... */ }); dd($usersQuery->toSql(), $usersQuery->getBindings());
在 Laravel Eloquent 中實(shí)現(xiàn)多條件、多關(guān)聯(lián)模型的復(fù)雜搜索,需要深入理解 with、whereHas 和 orWhereHas 的不同作用。核心要點(diǎn)在于:
通過遵循這些最佳實(shí)踐,您可以構(gòu)建出高效、健壯且易于維護(hù)的 Laravel 搜索功能。
以上就是Laravel Eloquent 高級(jí)搜索:解決多關(guān)聯(lián)模型與多條件查詢難題的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注php中文網(wǎng)其它相關(guān)文章!
每個(gè)人都需要一臺(tái)速度更快、更穩(wěn)定的 PC。隨著時(shí)間的推移,垃圾文件、舊注冊(cè)表數(shù)據(jù)和不必要的后臺(tái)進(jìn)程會(huì)占用資源并降低性能。幸運(yùn)的是,許多工具可以讓 Windows 保持平穩(wěn)運(yùn)行。
微信掃碼
關(guān)注PHP中文網(wǎng)服務(wù)號(hào)
QQ掃碼
加入技術(shù)交流群
Copyright 2014-2025 http://www.miracleart.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號(hào)