Weave 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 Make Scope Laravel Package

samasend/laravel-make-scope

A Laravel package that adds a make:scope Artisan command to quickly scaffold reusable Eloquent query scopes, generating the class and wiring it into your models so you can keep query logic clean and DRY.

View on GitHub
Deep Wiki
Context7

Getting Started

laravel-make-scope is a single-purpose Artisan generator: it adds php artisan make:scope {name} so you can extract a block of Eloquent query logic into a dedicated, testable class instead of piling up scopeX methods on the model.

Install (mind the archive status):

composer require samasend/laravel-make-scope --dev

It's a dev-time generator, so --dev is usually correct. Because the package is archived (last release July 2021), pin it and verify it resolves against your Laravel/PHP version before committing — on Laravel 10/11 you may need --ignore-platform-reqs or a replace/fork. If composer refuses, copy the stubs/ directory into your own app and hand-roll the provider.

Where to look first, in order:

  1. vendor/samasend/laravel-make-scope/src/ — the service provider (command registration) and the command class. This tells you the default namespace (App\Scopes), the output path, and whether a --model/--force option exists.
  2. vendor/samasend/laravel-make-scope/stubs/ — the actual template for the generated class. This is the source of truth. Read it before writing anything; the exact interface, method signature, and return type live here, and archived packages rarely match their README.
  3. composer.json under extra.laravel.providers — confirms auto-discovery. If absent, register Samasend\MakeScope\MakeScopeServiceProvider in config/app.php.

First use case — the classic "same where chain copied across three models" problem. You have Post::where('published', true)->where('published_at', '<=', now()) repeated in a controller, a console command, and a Nova resource. Run:

php artisan make:scope Published

Open the generated app/Scopes/Published.php, move the where chain into the body, and invoke it from a single place. That's the entire value proposition: one named, autoloaded, unit-testable home for a query fragment.

Implementation Patterns

One class per scope, not per model. Resist PostScopes god-classes. Published, Active, Recent, OwnedBy as separate classes compose better and diff cleanly in reviews. This is the pattern the generator nudges you toward and the one worth adopting even if you later replace the package.

Invoke scopes explicitly from the model. Since the generated class is a plain class (not a global scope), the idiomatic wiring is a thin trait or a small __call on the builder:

// app/Concerns/HasScopes.php
trait HasScopes
{
    public function scopeWithClass(Builder $query, string $scope, ...$args): Builder
    {
        return (new $scope(...$args))->apply($query, $this);
    }
}

Then Post::query()->withClass(Published::class)->get(). Confirm the generated method name/signature against the stub — if the package ships its own trait or a Scope::query() helper, use that instead of inventing one.

Parameterize via the constructor, not method args. Scopes with runtime values (a date cutoff, a user) are cleaner as new RecentPosts(now()->subWeek()) than as variadic method parameters. It keeps apply() to the two-argument contract Laravel's Scope interface already implies and makes the class instantiable in a test.

Compose in the right order. Scopes applied left-to-right chain onto the same builder, so Active → NotBanned → Recent reads top-down and each class stays ignorant of the others. Keep each apply() a pure where/with/orderBy addition with no side effects and no ->get() calls — a scope that terminates the query can't be composed or reused.

Reuse across models via a shared interface. If Published applies to Post and Article, type-hint an interface (Publishable) rather than checking instanceof. The scope class only ever sees a Builder + Model, so duck-typing on columns works, but a shared contract documents intent and catches mistakes at the call site.

Map API filters to scope classes. A ?filter=published query string maps cleanly to a whitelist:

$scopes = ['published' => Published::class, 'recent' => Recent::class];
if ($class = $scopes[$request->query('filter')] ?? null) {
    (new $class)->apply($query, $model);
}

Whitelist, never reflect on user input — that's the security boundary.

Test the scope, not the model. Because it's a standalone class, you can assert on the generated SQL without touching a database, or use RefreshDatabase and assert results. This is the strongest argument for the pattern over scopeX methods.

Gotchas and Tips

  • Archived means frozen against Laravel's internals. The package itself is thin, but the command extends Illuminate\Console\Command and the generated class may type-hint Illuminate\Database\Eloquent\Builder. Both are stable, so it usually keeps working — but there will be no fix when a future Laravel changes the Scope interface or the generator's getStub() path. Fork the stub into your repo on day one and treat the package as optional.

  • Don't confuse this with global scopes. The name "scope" is overloaded. Laravel's Illuminate\Database\Eloquent\Scope + addGlobalScope() applies automatically to every query; classes generated here are local, applied on demand. If you register one as a global scope you'll silently filter reads everywhere, including find() and relationship lazy-loads — a classic production incident.

  • Stub publishing may not exist. If php artisan vendor:publish shows nothing, the package ships no publishable stubs/config; copy the vendor stub manually and customize your own generator, or just edit generated files by hand. Check vendor/.../src/ServiceProvider for publishes() calls before assuming.

  • Name collisions with local scopes. make:scope Active creates App\Scopes\Active, but if the model also has scopeActive(), both exist and it's unclear which path a developer used. Pick one convention per codebase and delete the scopeX version when you extract it.

  • Return types matter. If the generated apply() declares : void but you return $builder->where(...), PHP fatals on the mismatch. Match the stub exactly; if it's void, mutate the builder and return nothing.

  • Auto-discovery is the usual silent failure. Command "not found" almost always means the provider isn't registered — either extra.laravel.providers is missing from the installed version or you're on a package-manifest cache. Run php artisan package:discover and php artisan clear-compiled before debugging the command itself.

  • Debugging a scope = dump the SQL, not the results. ->toSql() and ->getBindings() on the builder after apply() show whether the fragment landed; a scope returning a fresh Builder instead of mutating the passed one is the #1 bug and is invisible in result-based tests.

  • Extension points are limited by design. There is no scope registry, no discovery, no priority ordering. If you need conditional/composable query logic at scale, the maintained alternatives are native scopeX methods (zero dependency), spatie/laravel-query-builder (filter/sort/include from request), or a custom query-object layer. Adopt this package for the class-per-scope convention, not for any runtime capability — you can replicate the whole thing with one stub file and ten lines of Artisan.

  • Pin and audit. Archived, 101 stars, no security surface of its own (it only writes a file at dev time), but Composer will happily pull transitive dev dependencies. Keep it in require-dev and out of production builds.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo