一. 需要解決什么問題
最近Laravel 項目中遇到一個需求,我有一個客戶表,每個員工都有自己的客戶,但是自己只能看自己的客戶。
項目中,有很多功能需要查詢客戶列表,客戶詳情,查詢客戶入口很多,這些查詢出來的數據,無一例外都只能查自己的。
問題來了,每個查詢點
都去補充限制條件
,處理起來非常繁瑣
,而且維護成本很高
,容易出現問題
,一旦出現條件不到位,就可能造成不可估量的損失。
類似以上這種需求,應該有很多相似的。大家自覺往上靠。
提出疑問,可有辦法統一處理
,只維護一處即可,所有查詢都
帶上條件限制。
答案肯定是有,以下是其中一個解決思路,也是推薦的。
二. 什么是查詢作用域
查詢作用域(Query Scope
)是 Laravel Eloquent ORM
提供的一個強大功能,它允許你封裝常用的查詢邏輯,使代碼更簡潔、可重用。合理使用可以大幅提高代碼質量和開發效率。
三. 作用域怎么用
3.1 全局作用域
全局作用域可以為模型的所有查詢添加約束
。最常見的 軟刪除
功能就是利用全局范圍僅從數據庫中檢索「未刪除」模型。
3.1.1 開發全局作用域
編寫作用域類,目錄可以是任意目錄
,我創建在 App\Models\Scopes
下面
<?phpnamespace App\Models\Scopes;use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;class ClientScope implements Scope
{public function apply(Builder $builder, Model $model): void{$builder->where('uid_code', '=', get_admin_code());}
}
代碼解釋:
- 需要實現
Illuminate\Database\Eloquent\Scope
接口 Scope
接口要求實現apply
方法。需要完善 apply 方法apply
方法里面補充的就是所需要的限制條件
,例如我的就是指定 uid_code 等于 當前登錄用戶code
在需要模型里面,配置全局作用域
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Scopes\ClientScope;
use Illuminate\Database\Eloquent\Factories\HasFactory;class ClientModel extends Model
{use HasFactory;protected static function booted(): void{static::addGlobalScope(new ClientScope());}
}
代碼解釋:
- 重寫模型的
booted
方法并使用addGlobalScope
方法 addGlobalScope
方法可以接受作用域
的一個實例參數
,也就是上面編寫的作用域
完成上面的步驟即可,在使用 ClientModel
模型查詢時,都會
帶上作用域里面的條件。
3.1.2 匿名全局作用域
是不是感覺上面的代碼繁瑣
了,為了一個全局作用域還需要單獨去編寫一個類。那么使用閉包
來定義全局作用域,可以簡化
流程。
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;class ClientModel extends Model
{use HasFactory;protected static function booted(): void{static::addGlobalScope('clientScope', function (Builder $builder) {$builder->where('uid_code', '=', get_admin_code());});}
}
代碼解釋:
- 重寫模型的
booted
方法并使用addGlobalScope
方法 addGlobalScope
方法,第一個
參數為作用域名稱
,第二個
參數為匿名函數
,里面補充就是相關條件。
注意:
匿名全局作用域適用于單獨模型
使用,如果有多個
模型,都需要
同樣的限制條件,則還是需要創建
作用域類的。方便統一維護
。視情況而定
3.1.3 取消全局作用域
如果某個查詢不需要這個全局限制,那么就可以取消全局作用域
- 取消部分,或者指定作用域
//取消全局作用域類
ClientModel::withoutGlobalScope(ClientScope::class)->get();
//取消閉包作用域
ClientModel::withoutGlobalScope('clientScope')->get();
// 取消部分作用域...
ClientModel::withoutGlobalScopes([ClientScope::class, SecondScope::class
])->get();
- 取消全部作用域
// 取消全部全局作用域...
ClientModel::withoutGlobalScopes()->get();
3.2 局部作用域
局部作用域允許你定義通用的查詢約束
,可以鏈式調用
。
// 在模型中定義
class ClientModel extends Model
{public function scopePopular($query){return $query->where('votes', '>', 100);}public function scopeActive($query){return $query->where('active', 1);}
}
使用方法:
// 單個作用域
$users = ClientModel::popular()->get();// 鏈式調用多個作用域
$users = ClientModel::popular()->active()->orderBy('created_at')->get();//使用orWhere實現
$users = ClientModel::popular()->orWhere->active()->get();//使用閉包實現
$users = ClientModel::popular()->orWhere(function (Builder $query) {$query->active();
})->get();
3.3 動態作用域
動態作用域允許你傳遞參數
給作用域:
// 定義
class ClientModel extends Model
{public function scopeOfType($query, $type){return $query->where('type', $type);}
}
注意:作用域參數要放在 $query 參數之后
// 使用
$users = ClientModel::ofType('admin')->get();
四. 總結
查詢作用域是 Laravel Eloquent 中組織查詢邏輯的強大工具,合理使用可以大幅提高代碼質量和開發效率。
合理使用 全局作用域和局部作用域,可以使查詢邏輯更清晰易懂,方便維護,降低后期的查看查看成本。