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:

    composer require mpyw/laravel-local-class-scope
    

    No additional configuration is required—the package auto-registers its macro.

  2. First Use Case: Create a simple scope class (e.g., app/Scopes/ActiveScope.php):

    namespace App\Scopes;
    
    use Illuminate\Database\Eloquent\Builder;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\Scope;
    
    class ActiveScope implements Scope
    {
        public function apply(Builder $query, Model $model): void
        {
            $query->where('active', true);
        }
    }
    

    Use it in a query:

    User::scoped(ActiveScope::class)->get();
    

Where to Look First

  • Package README: Focus on the Usage section for syntax and examples.
  • Laravel Docs: Review Eloquent Query Scopes for the Scope interface.
  • Example Scopes: Check app/Scopes/ for existing scope classes in your project.

Implementation Patterns

Core Workflows

1. Basic Scope Implementation

  • Pattern: Convert repetitive where() clauses into reusable scope classes.
  • Example:
    // Before (Controller)
    User::where('status', 'active')->get();
    
    // After (Scope Class)
    User::scoped(ActiveScope::class)->get();
    
  • Workflow:
    1. Identify repetitive query logic.
    2. Create a Scope class implementing apply().
    3. Use scoped() in queries.

2. Parameterized Scopes

  • Pattern: Pass dynamic values to scopes via constructor.
  • Example:
    // Scope Class
    class AgeScope implements Scope
    {
        public function __construct(public int $minAge) {}
    
        public function apply(Builder $query, Model $model): void
        {
            $query->where('age', '>=', $this->minAge);
        }
    }
    
    // Usage
    User::scoped([AgeScope::class, 18])->get();
    
  • Tip: Use array syntax ([Class::class, arg1, arg2]) for clarity with multiple arguments.

3. Combining Scopes

  • Pattern: Chain scopes for complex queries.
  • Example:
    User::scoped(ActiveScope::class)
        ->scoped([AgeScope::class, 18])
        ->get();
    
  • Note: Order matters—later scopes override earlier ones for the same column.

4. Local Method Scopes

  • Pattern: Expose scopes as model methods for fluent syntax.
  • Example:
    // Model
    class User extends Model
    {
        public function scopeActive(Builder $query): Builder
        {
            return $this->scoped(ActiveScope::class);
        }
    }
    
    // Usage
    User::active()->get();
    
  • Best Practice: Group related local scopes in a trait for reuse across models.

5. Testing Scopes

  • Pattern: Isolate scope logic for unit tests.
  • Example:
    public function test_active_scope()
    {
        $scope = new ActiveScope();
        $query = User::query();
        $scope->apply($query, new User());
    
        $query->getDump(); // Debug the generated query
    }
    

Integration Tips

  • Namespace Organization: Place scope classes in app/Scopes/ and use fully qualified names (e.g., App\Scopes\ActiveScope) to avoid collisions.

  • Dependency Injection: Inject services into scopes via constructor:

    class TenantScope implements Scope
    {
        public function __construct(private TenantRepository $tenantRepo) {}
    
        public function apply(Builder $query, Model $model): void
        {
            $tenantId = $this->tenantRepo->getId();
            $query->where('tenant_id', $tenantId);
        }
    }
    
  • Global vs. Local Scopes: Use global scopes for mandatory filters (e.g., soft deletes) and local scopes for optional ones (e.g., active()).

  • Performance: For heavy queries, benchmark class-based scopes against closures. The macro adds negligible overhead (~1ms).

  • IDE Support: Leverage IDE autocompletion for scope classes (e.g., User::scoped(**)).


Gotchas and Tips

Pitfalls

  1. Class Not Found Errors:

    • Cause: Forgetting to autoload the scope class or using an unqualified name.
    • Fix: Use fully qualified names (e.g., App\Scopes\ActiveScope::class).
  2. Constructor Argument Mismatches:

    • Cause: Passing wrong arguments to parameterized scopes.
    • Fix: Use named arguments or type hints:
      User::scoped([AgeScope::class, minAge: 18])->get();
      
  3. Scope Overrides:

    • Cause: Multiple scopes targeting the same column (e.g., two where('active', ...)).
    • Fix: Document scope precedence or use orWhere() for additive logic.
  4. Macro Conflicts:

    • Cause: Another package overrides the scoped macro.
    • Fix: Check for macro conflicts with Macroable::hasMacro('scoped').
  5. Global Scope Interference:

    • Cause: Local scopes may conflict with global scopes (e.g., soft deletes).
    • Fix: Disable global scopes temporarily:
      User::withoutGlobalScopes()->scoped(ActiveScope::class)->get();
      

Debugging Tips

  • Inspect Generated Queries: Use $query->toSql() or Laravel Debugbar to verify scope behavior.

  • Log Scope Execution: Add logging in apply():

    public function apply(Builder $query, Model $model): void
    {
        logger()->debug('Applying ActiveScope', ['query' => $query->toSql()]);
        $query->where('active', true);
    }
    
  • Test Edge Cases:

    • Empty results.
    • Null/undefined parameters.
    • Concurrent scope application.

Configuration Quirks

  • No Config File: The package is zero-config. All behavior is driven by the macro.
  • Macro Override: To customize the macro, publish the package’s config (if available) or extend it:
    Builder::macro('scoped', function ($scope, ...$args) {
        // Custom logic
        return $this->macroScope($scope, $args);
    });
    

Extension Points

  1. Custom Scope Interface: Extend the Scope interface for project-specific needs:

    interface ProjectScope extends Scope
    {
        public function getName(): string;
    }
    
  2. Scope Registry: Create a service to register and resolve scopes dynamically:

    class ScopeRegistry
    {
        public function resolve(string $scopeClass, array $args): Scope
        {
            return new $scopeClass(...$args);
        }
    }
    
  3. Scope Groups: Combine multiple scopes into a single class for complex queries:

    class AdminUserScope implements Scope
    {
        public function apply(Builder $query, Model $model): void
        {
            $query->scoped(ActiveScope::class)
                  ->scoped([AgeScope::class, 21]);
        }
    }
    
  4. Dynamic Scope Resolution: Use a factory pattern to resolve scopes by name:

    User::scoped('active')->get(); // Resolves to ActiveScope
    

    (Requires custom macro implementation.)

Pro Tips

  • Naming Conventions: Use CamelCase for scope classes (e.g., ActiveScope) and snake_case for local methods (e.g., scope_active).

  • Document Scopes: Add PHPDoc to scope classes to clarify usage:

    /**
     * Filters users by active status.
     *
     * @param Builder $query
     * @param Model   $model
     */
    class ActiveScope implements Scope { ... }
    
  • Leverage Traits for Local Scopes: Share local scope methods across models:

    trait ScopesActive
    {
        public function scopeActive(Builder $query): Builder
        {
            return $this->scoped(ActiveScope::class);
        }
    }
    
  • Use for API Filtering: Map API query params to scopes dynamically:

    $scopeClass = match ($request->filter) {
        'active' => ActiveScope::class,
        'age'    => AgeScope::class,
        default  => null,
    };
    
    if ($scopeClass) {
        User::scoped($scopeClass, ...$request->filterArgs)->get();
    }
    
  • **Avoid Over

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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle