Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Scopes Laravel Package

curly-deni/laravel-scopes

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require curly-deni/laravel-scopes
    

    No publisher or service provider needed—just use the traits directly.

  2. 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;
    
        // ...
    }
    
  3. First Use Case:

    • Ownership Scope: Automatically filters queries to only return models owned by the authenticated user.
      $posts = Post::all(); // Only returns posts where `user_id = auth()->id()`
      
    • Public Scope: Filters models by is_public status (default field name).
      $publicPosts = Post::public()->get(); // Only returns posts where `is_public = true`
      
  4. Where to Look First:


Implementation Patterns

Usage Patterns

  1. Combining Scopes: Use multiple traits on a single model for layered visibility control:

    class Post extends Model
    {
        use HasOwnershipScope, HasPublicScope, HasSoftDeletes;
    }
    
    • Queries now respect ownership, public status, and soft deletes automatically.
  2. 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
            ));
        }
    }
    
  3. Disabling Scopes Temporarily: Use withoutGlobalScopes() when you need raw queries:

    $allPosts = Post::withoutGlobalScopes()->get();
    
  4. 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'));
    }
    
  5. 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'));
        }
    }
    

Workflows

  • 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();
    }
    

Integration Tips

  1. Laravel Nova: Scopes work out-of-the-box with Nova’s resource queries. No additional config needed.

  2. Testing: Mock the authenticated user to test scopes:

    $user = User::factory()->create();
    $this->actingAs($user);
    $posts = Post::all(); // Only returns $user's posts
    
  3. Database Views: Create a view that bypasses scopes for reporting:

    CREATE VIEW all_posts AS SELECT * FROM posts;
    
    $rawPosts = DB::table('all_posts')->get();
    

Gotchas and Tips

Pitfalls

  1. Field Name Mismatches:

    • Issue: Scopes assume user_id for ownership and is_public for visibility.
    • Fix: Override the trait’s boot method (as shown in Custom Field Names) or extend the scope class.
  2. Over-Eager Loading:

    • Issue: Scopes apply to all queries, including eager loads.
    • Fix: Use with() explicitly or disable scopes for specific relationships:
      $posts = Post::with(['comments' => function ($query) {
          $query->withoutGlobalScopes();
      }])->get();
      
  3. Policy Conflicts:

    • Issue: Policies might override or conflict with scopes.
    • Fix: Ensure policies use 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; }
      
  4. Nested Scopes:

    • Issue: Combining multiple scopes might lead to unexpected AND/OR logic.
    • Fix: Test edge cases (e.g., a public post owned by another user):
      $post = Post::where('user_id', '!=', auth()->id())
                  ->where('is_public', true)
                  ->first();
      

Debugging

  1. Inspect Raw Queries: Use Laravel’s query logging to verify scopes:

    DB::enableQueryLog();
    Post::all();
    dd(DB::getQueryLog());
    
  2. Disable Scopes for Debugging: Temporarily remove global scopes to isolate issues:

    Post::withoutGlobalScopes()->get();
    
  3. Check for Overrides: Ensure no other global scopes are interfering:

    Post::getQuery()->getRawSql(); // Inspect the final query
    

Config Quirks

  1. No Configuration File:

    • The package is zero-config. All behavior is driven by traits.
  2. Dynamic User Context:

    • Scopes rely on auth()->user(). For non-web contexts (e.g., APIs), ensure the user is set:
      Auth::login($user); // Manually set user for testing
      

Extension Points

  1. 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'));
        }
    }
    
  2. Conditional Scopes: Dynamically enable/disable scopes based on context:

    class Post extends Model
    {
        public static function boot()
        {
            if (request()->has('admin')) {
                static::withoutGlobalScopes();
            }
        }
    }
    
  3. Third-Party Integration:

    • Laravel Filament: Scopes work with Filament’s table queries. Use withoutGlobalScopes() in admin panels.
    • Spatie Media Library: Combine with HasOwnershipScope to restrict media access:
      class Media extends Model
      {
          use HasOwnershipScope;
      }
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai