safemood / laravel-magic-scopes
Laravel Magic Scopes auto-generates query scopes for your models — no code needed, just magic 🔮.
Fund package maintenance!
Khalil Bouzidi
Requires
- php: ^8.4|^8.3
- illuminate/contracts: ^10.0|^11.0|^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^2.9 || ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^8.0|^9.0|^10.0
- pestphp/pest: ^2.34|^3.0
- pestphp/pest-plugin-arch: ^3.0||^2.0
- pestphp/pest-plugin-laravel: ^3.0||^2.0
- phpstan/extension-installer: ^1.3
- phpstan/phpstan-deprecation-rules: ^1.1||^2.0
- phpstan/phpstan-phpunit: ^1.3||^2.0
This package is auto-updated.
Last update: 2025-06-05 12:55:48 UTC
README
Laravel Magic Scopes auto-generates query scopes for your models — no code needed, just magic 🔮.
Table of Contents
Installation
You can install the package via composer:
composer require safemood/laravel-magic-scopes
You can publish the config file with:
php artisan vendor:publish --tag="magic-scopes-config"
This is the contents of the published config file:
<?php declare(strict_types=1); return [ 'resolvers' => [ \Safemood\MagicScopes\Resolvers\BooleanFieldScopeResolver::class, \Safemood\MagicScopes\Resolvers\DateFieldScopeResolver::class, \Safemood\MagicScopes\Resolvers\EnumFieldScopeResolver::class, \Safemood\MagicScopes\Resolvers\ForeignKeyFieldScopeResolver::class, \Safemood\MagicScopes\Resolvers\JsonFieldScopeResolver::class, \Safemood\MagicScopes\Resolvers\NumberFieldScopeResolver::class, \Safemood\MagicScopes\Resolvers\StringFieldScopeResolver::class, ], ];
Usage
Simply use the HasMagicScopes trait in your Eloquent model:
use Safemood\MagicScopes\Traits\HasMagicScopes; class Post extends Model { use HasMagicScopes; }
What It can do
$posts = Post::where('views', '>', 216) // query ->notPublished() // magic scope ->recent() // real scope ->createdAfter('2025-05-01') // magic scope ->get();
Boolean Field Scopes
Post::published()->get(); // where('is_published', true) Post::notPublished()->get(); // where('is_published', false) Post::hasFeaturedImage()->get(); // where('has_featured_image', true) Post::hasNotFeaturedImage()->get(); // where('has_featured_image', false) Post::sticky()->get(); // where('is_sticky', true) Post::notSticky()->get(); // where('is_sticky', false)
Enum Field Scopes
Post::statusIs('published')->get(); // where('status', 'published') Post::typeIs('announcement')->get(); // where('type', 'announcement')
Foreign Key Scopes
Post::forUser(1)->get(); // where('user_id', 1) Post::forCategory([1, 2])->get(); // whereIn('category_id', [1, 2]) Post::withUser()->get(); // with('user') Post::withUser([1, 2])->get(); // with(['user' => fn ($q) => $q->whereIn('id', [1, 2])]) Post::withAuthor(10)->get(); // with(['author' => fn ($q) => $q->where('id', 10)]) NOTE: The relationships ('user', 'author') must be properly defined on the `Post` model (at least for now 😉)
JSON Field Scopes
Post::rentRequestsContains('rooms_count', 2)->get(); // whereJsonContains('rent_requests->rooms_count', 2) Post::rentContains('city', 'Tunis')->get(); // whereJsonContains('rent->city', 'Tunis') Post::settingsContains('timezone', 'UTC')->get(); // whereJsonContains('settings->timezone', 'UTC')
Number Field Scopes
Post::whereViewsGreaterThan(50)->get(); // where('views', '>', 50) Post::whereScoreEquals(90)->get(); // where('score', '=', 90) Post::wherePriceBetween(100, 200)->get(); // whereBetween('price', [100, 200]) Post::whereDownloadsEquals(10)->get(); // where('downloads', '=', 10)
Date Field Scopes
Post::reviewedAt('2024-05-10')->get(); // Equivalent to: whereDate('reviewed_at', '2024-05-10') Post::reviewedBefore('2024-05-15')->get(); // Equivalent to: whereDate('reviewed_at', '<', '2024-05-15') Post::reviewedAfter('2024-05-15')->get(); // Equivalent to: whereDate('reviewed_at', '>', '2024-05-15') Post::reviewedBetween('2024-05-10', '2024-05-20')->get(); // Equivalent to: whereBetween('reviewed_at', ['2024-05-10', '2024-05-20'])
Extend
You can create your own custom scope resolver if you want to control how scopes are resolved from method names.
Here's how to create a custom ScopeResolver:
⚠️ Important:
Custom resolvers should not match the same method as another resolver.
If multiple resolvers match, an exception is thrown to avoid conflicts.
<?php declare(strict_types=1); namespace Safemood\MagicScopes\Resolvers; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Str; use Safemood\MagicScopes\Contracts\ScopeResolverContract; class CustomScopeResolver implements ScopeResolverContract { public function matches(string $method, Builder $builder): bool { return Str::startWith($method, 'customPrefix'); } public function apply(Builder $query, string $method, array $parameters, Model $model): Builder { $column = 'your_column_name'; // extract from method name $value = $parameters[0] ?? null; // get the value, depending on your logic $builder->where($column, $value); // Apply basic WHERE condition return $query; // Always return the original query builder after applying conditions } }
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.