Installation:
composer require curly-deni/laravel-scopes
No publisher or service provider needed—just use the traits directly.
Apply to a Model:
Add the relevant trait(s) to your Eloquent model (e.g., Post.php):
use Aesis\Scopes\Traits\HasOwnershipScope;
use Aesis\Scopes\Traits\HasPublicScope;
class Post extends Model
{
use HasOwnershipScope, HasPublicScope;
// ...
}
First Use Case:
$posts = Post::all(); // Only returns posts where `user_id = auth()->id()`
is_public status (default field name).
$publicPosts = Post::public()->get(); // Only returns posts where `is_public = true`
Where to Look First:
Traits/ directory.Combining Scopes: Use multiple traits on a single model for layered visibility control:
class Post extends Model
{
use HasOwnershipScope, HasPublicScope, HasSoftDeletes;
}
Custom Field Names:
Override default field names (e.g., owner_id instead of user_id):
class Post extends Model
{
use HasOwnershipScope {
HasOwnershipScope::boot as private bootOwnership;
}
protected static function bootHasOwnershipScope()
{
static::addGlobalScope(new \Aesis\Scopes\Scopes\OwnershipScope(
static::class,
'owner_id' // Custom field
));
}
}
Disabling Scopes Temporarily:
Use withoutGlobalScopes() when you need raw queries:
$allPosts = Post::withoutGlobalScopes()->get();
Integration with Policies:
Pair with Laravel’s authorize() or can() for business logic:
public function show(Post $post)
{
$this->authorize('view', $post); // Uses ownership/public scopes implicitly
return view('posts.show', compact('post'));
}
Dynamic Scopes: Extend traits to add dynamic conditions (e.g., team ownership):
use Aesis\Scopes\Traits\HasOwnershipScope;
class Post extends Model
{
use HasOwnershipScope;
public function scopeTeam($query)
{
return $query->whereIn('user_id', auth()->user()->teams->pluck('id'));
}
}
CRUD Operations:
Scopes auto-apply to all(), get(), find(), etc. No manual where clauses needed.
// Controller
public function index()
{
return Post::latest()->get(); // Automatically scoped
}
API Endpoints:
Use public() scope for endpoints requiring visibility control:
Route::get('/posts/public', function () {
return Post::public()->get();
});
Admin Panels: Disable scopes in admin contexts:
public function adminIndex()
{
return Post::withoutGlobalScopes()->get();
}
Laravel Nova: Scopes work out-of-the-box with Nova’s resource queries. No additional config needed.
Testing: Mock the authenticated user to test scopes:
$user = User::factory()->create();
$this->actingAs($user);
$posts = Post::all(); // Only returns $user's posts
Database Views: Create a view that bypasses scopes for reporting:
CREATE VIEW all_posts AS SELECT * FROM posts;
$rawPosts = DB::table('all_posts')->get();
Field Name Mismatches:
user_id for ownership and is_public for visibility.boot method (as shown in Custom Field Names) or extend the scope class.Over-Eager Loading:
with() explicitly or disable scopes for specific relationships:
$posts = Post::with(['comments' => function ($query) {
$query->withoutGlobalScopes();
}])->get();
Policy Conflicts:
authorize() with the model, not raw attributes:
// Bad: Checks `is_public` directly (bypasses scopes)
public function viewAny(User $user) { return $user->is_public; }
// Good: Uses model (scopes applied)
public function viewAny(User $user, Post $post) { return true; }
Nested Scopes:
AND/OR logic.$post = Post::where('user_id', '!=', auth()->id())
->where('is_public', true)
->first();
Inspect Raw Queries: Use Laravel’s query logging to verify scopes:
DB::enableQueryLog();
Post::all();
dd(DB::getQueryLog());
Disable Scopes for Debugging: Temporarily remove global scopes to isolate issues:
Post::withoutGlobalScopes()->get();
Check for Overrides: Ensure no other global scopes are interfering:
Post::getQuery()->getRawSql(); // Inspect the final query
No Configuration File:
Dynamic User Context:
auth()->user(). For non-web contexts (e.g., APIs), ensure the user is set:
Auth::login($user); // Manually set user for testing
Custom Scopes: Extend the base scope classes to add logic:
namespace App\Scopes;
use Aesis\Scopes\Scopes\OwnershipScope;
class TeamOwnershipScope extends OwnershipScope
{
protected function applyOwnershipRestrictions()
{
$this->query->whereIn('user_id', auth()->user()->teamMembers->pluck('id'));
}
}
Conditional Scopes: Dynamically enable/disable scopes based on context:
class Post extends Model
{
public static function boot()
{
if (request()->has('admin')) {
static::withoutGlobalScopes();
}
}
}
Third-Party Integration:
withoutGlobalScopes() in admin panels.HasOwnershipScope to restrict media access:
class Media extends Model
{
use HasOwnershipScope;
}
How can I help you explore Laravel packages today?