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 Local Class Scope Laravel Package

mpyw/laravel-local-class-scope

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation: Run composer require mpyw/laravel-local-class-scope in your Laravel 11/12 project.
  2. Register Macro: The package auto-registers a scoped() macro for Eloquent query builders—no manual configuration needed.
  3. First Use Case: Create a simple scope class (e.g., ActiveScope) implementing Illuminate\Database\Eloquent\Scope with an apply() method. Use it locally:
    User::scoped(ActiveScope::class)->get();
    

Where to Look First


Implementation Patterns

Core Workflows

  1. Reusable Global Scopes as Local Scopes: Convert a global scope (registered via protected $with = [...]) into a local scope dynamically:

    // Global scope (e.g., in User model)
    protected static $with = [ActiveScope::class];
    
    // Local usage
    User::scoped(ActiveScope::class)->get(); // Applies only to this query
    
  2. Parameterized Scopes: Pass arguments to scopes via constructor:

    class AgeScope implements Scope {
        public function __construct(int $minAge, int $maxAge) {}
        public function apply(Builder $query, Model $model) {
            $query->whereBetween('age', [$minAge, $maxAge]);
        }
    }
    // Usage
    User::scoped(new AgeScope(18, 30))->get();
    
  3. Chaining Scopes: Combine multiple scopes in a single query:

    User::scoped(ActiveScope::class)
        ->scoped(new AgeScope(18, 30))
        ->get();
    
  4. Dynamic Scope Resolution: Use dependency injection for scopes (e.g., via Laravel’s container):

    class TenantScope implements Scope {
        public function __construct(private TenantResolver $resolver) {}
        public function apply(Builder $query, Model $model) {
            $query->where('tenant_id', $this->resolver->getId());
        }
    }
    // Usage
    User::scoped(new TenantScope(app(TenantResolver::class)))->get();
    

Integration Tips

  • Testing: Mock scopes in tests to isolate query logic:
    $scope = $this->createMock(Scope::class);
    $scope->method('apply')->willReturnCallback(function ($query) {
        $query->where('test', true);
    });
    User::scoped($scope)->get(); // Test with mocked scope
    
  • Performance: Avoid instantiating heavy scopes repeatedly. Cache or reuse instances where possible.
  • IDE Support: Use @mixin in PHPStorm for better autocompletion with dynamic scopes:
    /** @mixin \Illuminate\Database\Eloquent\Builder */
    class User extends Model { ... }
    

Gotchas and Tips

Pitfalls

  1. Constructor Injection: Scopes passed as strings (e.g., User::scoped('App\Scopes\ActiveScope')) cannot receive constructor arguments. Always use new for parameterized scopes:

    // ❌ Fails (no args)
    User::scoped('App\Scopes\AgeScope');
    
    // ✅ Works
    User::scoped(new AgeScope(18, 30));
    
  2. Model Binding: The Model parameter in apply() may be null if the scope is applied to a query builder without a model (e.g., DB::table('users')->scoped(...)). Handle this gracefully:

    public function apply(Builder $query, ?Model $model): void {
        if ($model) {
            $query->where('user_id', $model->id);
        }
    }
    
  3. Macro Overrides: The scoped() macro replaces the default behavior. If you later need the original scope() method (e.g., for global scopes), you’ll need to restore it or use a different method name.

  4. PHP 8.2+ Requirement: Older Laravel versions (pre-11) or PHP <8.2 will fail. Use ^11.0 || ^12.0 as specified.

Debugging

  • Query Logs: Enable Laravel’s query logging to verify scopes are applied:
    DB::enableQueryLog();
    User::scoped(ActiveScope::class)->get();
    dd(DB::getQueryLog());
    
  • Scope Order: Scopes are applied in the order they’re chained. Use tap() to debug:
    User::scoped(ActiveScope::class)
        ->tap(fn($query) => dd($query->toSql()))
        ->get();
    

Tips

  1. Naming Conventions: Append Scope to class names (e.g., ActiveScope) for clarity and consistency with Laravel’s global scopes.

  2. Extending Scopes: Create base scope classes to share logic:

    abstract class BaseSoftDeletesScope implements Scope {
        public function apply(Builder $query, Model $model): void {
            $query->whereNull('deleted_at');
        }
    }
    
  3. Local vs. Global: Use local scopes for query-specific filters (e.g., admin dashboard queries) and global scopes for model-wide defaults (e.g., soft deletes).

  4. Type Safety: Use PHP 8.1+ attributes to enforce scope contracts:

    #[Attribute(Attribute::TARGET_CLASS)]
    class ScopeAttribute {}
    // Then validate scopes in tests or via static analysis.
    
  5. Performance: For complex scopes, consider compiling them into raw SQL or using query hints to reduce overhead:

    public function apply(Builder $query, Model $model): void {
        $query->whereRaw('...')->hint('SKIP_SCOPE_ANALYSIS');
    }
    
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.
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
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