lacodix/laravel-global-or-scope
Add multiple Eloquent global scopes that are grouped and applied with OR logic instead of the default AND. Use a simple trait to register OR-scopes and optionally disable some or all of them per query with withoutGlobalOrScopes().
composer require lacodix/laravel-global-or-scope
use Lacodix\LaravelGlobalOrScope\Traits\GlobalOrScope;
class Post extends Model
{
use GlobalOrScope;
}
booting() method:
public static function booting(): void
{
static::addGlobalOrScopes([Scope1::class, Scope2::class]);
}
Apply a query with additional conditions while leveraging OR logic for global scopes:
Post::query()->where('user_id', 1000)->get();
// Generates: `where user_id = 1000 AND (scope1 OR scope2)`
Define Scopes:
class ActiveScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
return $builder->where('is_active', true);
}
}
class DraftScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
return $builder->where('is_draft', true);
}
}
Register Scopes:
class Post extends Model
{
use GlobalOrScope;
public static function booting(): void
{
static::addGlobalOrScopes([
ActiveScope::class,
DraftScope::class
]);
}
}
Query with OR Logic:
// Applies: `WHERE (is_active = true OR is_draft = true)`
Post::query()->get();
Post::query()->withoutGlobalOrScopes()->get();
Post::query()->withoutGlobalOrScope(ActiveScope::class)->get();
Post::clearGlobalOrScopes();
Combine with Regular Global Scopes:
class Post extends Model
{
use GlobalOrScope;
public static function boot(): void
{
static::addGlobalScope(SoftDeletes::class); // Regular scope
static::addGlobalOrScopes([ActiveScope::class, DraftScope::class]);
parent::boot();
}
}
Runtime Scope Addition:
Post::query()->withGlobalOrScopes([new ArchivedScope()]);
Nested OR Logic:
$orScope1 = new OrScope([new ActiveScope(), new DraftScope()]);
$orScope2 = new OrScope([new PublishedScope(), new ScheduledScope()]);
Post::query()
->withGlobalScope('active_or_draft', $orScope1)
->withGlobalScope('published_or_scheduled', $orScope2);
Scope Registration Timing:
parent::boot() in the boot() method, or use booting().public static function boot(): void
{
parent::boot(); // Scopes won't apply!
static::addGlobalOrScopes([...]);
}
Scope Instantiation:
withGlobalOrScopes():
// Correct:
Post::query()->withGlobalOrScopes([new ActiveScope(), new DraftScope()]);
// Incorrect (throws error):
Post::query()->withGlobalOrScopes([ActiveScope::class, DraftScope::class]);
SQL Injection Risk:
// Risky:
$scope = new class implements Scope {
public function apply(Builder $builder, Model $model) {
$builder->where('column', $_GET['value']); // UNSAFE!
}
};
Inspect Disabled Scopes:
$disabledScopes = Post::query()->removedOrScopes();
dd($disabledScopes);
Verify Scope Application:
toSql() to inspect the generated query:
dd(Post::query()->toSql());
Clear Cached Scopes:
Post::clearGlobalOrScopes();
Custom Scope Logic:
OrScope for complex OR conditions:
class ComplexOrScope extends OrScope
{
public function apply(Builder $builder, Model $model)
{
$builder->where(function($q) {
foreach ($this->scopes as $scope) {
$scope->apply($q, $model);
$q->orWhere(...);
}
});
}
}
Conditional Scope Registration:
public static function booting(): void
{
if (auth()->check()) {
static::addGlobalOrScopes([UserSpecificScope::class]);
}
}
Scope Prioritization:
withoutGlobalOrScope() before withGlobalOrScopes() to override:
Post::query()
->withoutGlobalOrScope(ActiveScope::class)
->withGlobalOrScopes([new ArchivedScope()]);
How can I help you explore Laravel packages today?