라라벨 Query Scope
는 두가지 방식이 존재합니다.
- Local Scope
- Global Scope
Query Scope
로 쉽게 재사용 가능한 쿼리 제약 조건을 정의할 수 있습니다.
–
Local Scope
스코프를 정의하려면 모델 메서드 앞에 Scope
키워드를 접두사에 포함하면 됩니다.
아래는 User
모델에 상태(status) 조건을 정의하는 Scope
입니다.
/**
* Local Scope
*
* @param Builder $query
* @return mixed
*/
public function scopeActive(Builder $query): Builder
{
return $query->whereStatus(StatusEnum::ACTIVE->value);
}
Local Scope
를 활용하려면 해당 모델에 Method Chaining
방식으로 호출하면 됩니다.
$users = User::query()
->active()
->get();
scopeActive
스코프는 상태 (status) 가 active 인 데이터만 조회하게 됩니다.
하지만 파라미터로 상태 값을 넘겨 조회하고 싶다고 하면 Dynamic Scope
를 사용하면 됩니다.
/**
* Local Scope (Dynamic Scope)
*
* @param Builder $query
* @param int $status
* @return Builder
*/
public function scopeStatus(Builder $query, int $status): Builder
{
return $query->whereStatus($status);
}
조회 시 Method Chaning
방식은 동일하나 호출 시 파라미터를 포함할 수 있습니다.
$users = User::query()
->status(StatusEnum::INACTIVE->value)
->get();
Global Scope
모델에서 Global Scope
를 사용한다면 모든 쿼리에 해당 제약 조건을 정의할 수 있습니다.
Artisan
명령어로 Global Scope
파일 생성이 가능합니다.
$ php artisan make:scope ActiveStatusScope
Global Scope
는 위 명령어로 생성한 클래스의 apply
메서드만 구현하면 됩니다.
상태 (status) 가 active 인 데이터만 조회할 수 있도록 제약 조건을 정의합니다.
class ActiveStatusScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->where('status', StatusEnum::ACTIVE->value);
}
}
Global Scope
를 사용할 모델에서 스코프를 적용하기 위해서는 booted
메서드를 재정의하고 addGlobalScope
메서드에 생성한 스코프의 인스턴스를 생성합니다.
protected static function booted()
{
static::addGlobalScope(new ActiveStatusScope);
}
Global Scope
는 Local Scope
와 다르게 모든 쿼리에 apply
메서드에 구현한 조건이 사용됩니다.
$users = User::query()
->get();
위 쿼리의 결과는 where
절이 포함되어 있습니다.
array:1 [
0 => array:3 [
"query" => "select * from `users` where `status` = ?"
"bindings" => array:1 [
0 => 1
]
"time" => 1054.94
]
]
상황에 따라 Global Scope
사용이 불필요한 경우가 있습니다.
그 경우 모델 사용시에 withoutGlobalScope
메서드에 스코프 클래스 자체를 파라미터로 넘겨주면 됩니다.
$users = User::query()
->withoutGlobalScope(ActiveStatusScope::class)
->get();
위 쿼리의 결과는 withoutGlobalScope
로 정의한 스코프를 제거했기 때문에 ActiveStatusScope
스코프의 제약 조건을 포함하고 있지 않습니다.
array:1 [
0 => array:3 [
"query" => "select * from `users`"
"bindings" => []
"time" => 115.29
]
]
마침
Laravel Deepdive 저장소에서 사용된 예제 코드를 확인할 수 있습니다.
참고
https://laravel.com/docs/9.x/eloquent#query-scopes