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 Eloquent Scope As Select Laravel Package

protonemedia/laravel-eloquent-scope-as-select

Laravel Eloquent Scope as Select lets you add local scopes as select subqueries, so aggregated or computed values from related models come back in a single query. Skip extra database hits and N+1 problems while keeping your models clean and readable.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What the package actually does. protonemedia/laravel-eloquent-scope-as-select is a query-construction utility, not a framework component. It lets an existing Eloquent scope or constraint closure be appended to a parent query as a correlated subquery — typically as a derived column (SELECT ... , (SELECT ...) AS alias) or as a filter — so the scope's logic lives in one place instead of being re-implemented as addSelect, withCount, or raw SQL. The value proposition is DRY-ness across the codebase, not new database capability.

Architecture fit. Narrow and correct for a Laravel monolith or modular Laravel app that already centralizes query logic in scopes. It sits entirely at the Eloquent builder layer: no service provider bootstrapping beyond package discovery, no migrations, no schema, no runtime middleware, no queue or cache interaction. That means it is architecturally inert — it cannot destabilize the app's structure, and it can be removed without data consequences. Fit is poor for teams that do not use scopes heavily, that favor native withCount/withExists/withSum (Laravel 9+), or that maintain a query-builder abstraction in another language/ORM.

Integration feasibility. High. Composer install plus auto-discovery; adoption is per-call-site and reversible. The real feasibility question is not "can we install it" but "does the generated SQL perform." Because the package emits correlated subqueries, the integration effort is dominated by query review, not by wiring. Budget a short spike: install, convert one representative scope, capture toSql() with bindings, run EXPLAIN, compare against the current hand-written equivalent. If that passes on a realistic data volume, rollout is cosmetic. If it fails, the failure is visible immediately and cheaply.

Technical risk.

  • Binding and ordering fragility. Subquery bindings must merge into the parent query in the right position; combining this with whereRaw/selectRaw, orderByRaw, or nested where groups is the most likely source of subtle wrong-result or Invalid parameter number errors. Any adoption needs integration tests that assert generated SQL, not just result counts.
  • Column ambiguity. Correlated subqueries need qualified columns and explicit aliases. Sloppy scopes (unqualified where('active', true) across joined tables) will produce ambiguous-column errors or silently pick the wrong table.
  • Semantics of orderBy/limit inside a subquery differ by context (scalar select vs. whereIn vs. exists). Scopes written for top-level use may not be valid as-is; this is exactly the kind of thing to enumerate before committing.
  • Version coupling. Eloquent builder internals change across Laravel majors. A small package with 142 stars and 0 dependents has limited ecosystem pressure to keep pace; a Laravel upgrade may require an upstream fix or an internal patch.
  • Bus factor. Single-maintainer, MIT, small codebase. The code is forkable in an afternoon, which caps the risk, but there is no commercial support path.
  • Not a performance feature. It is easy for a developer to reach for this in a hot path where a denormalized counter or a native withCount would be materially faster.

Key questions for the spike.

  1. Which specific scopes do we intend to reuse, and how many of them contain joins, groupBy, having, or orderBy+limit?
  2. What is the exact public API surface (addScopeAsSelect signature, trait vs. builder macro, alias handling) in the pinned version, and is it stable across the Laravel versions we run?
  3. Can we retrieve the generated SQL and bindings for logging and EXPLAIN in CI?
  4. What is the row count and index coverage on the tables these subqueries will hit?
  5. Does the package handle global scopes and soft deletes consistently with the parent query?
  6. Is withCount/withExists/native addSelect sufficient for the majority of our cases, leaving only a handful that genuinely need scope reuse?

Integration Approach

Stack fit. Laravel 9/10/11, PHP 8.1+, MySQL/PostgreSQL/SQLite. Fits cleanly into an existing Eloquent-centric codebase with a repository or query-object layer. It does not fit applications that route reads through a non-Eloquent layer (raw query builders, stored procedures, a read model in another service), since the package is only meaningful when scopes are the unit of reuse.

Migration path. There is no data migration. The migration is conceptual: move duplicated constraint logic out of PHP-side ID arrays and bespoke addSelect closures and back into the canonical scope. A pragmatic three-step path:

  1. Shadow phase — keep existing query code, add the scope-as-select version alongside it behind a feature flag or in a test-only path, and assert both return identical result sets across a seeded fixture. This catches ambiguity and semantics problems with zero production exposure.
  2. Parallel phase — flip one low-traffic, read-only endpoint. Instrument with DB::listen or Telescope, capture execution time and EXPLAIN, compare against baseline.
  3. Cutover — replace the duplicated logic, delete it, and record the decision in an ADR.

Compatibility. Pin the exact version in composer.json, commit the lockfile, and add a composer outdated check to the upgrade runbook. Verify against our current Laravel minor before adoption and again before each Laravel major. If the package lags a Laravel release we need, the fallback is a vendored fork or replacement with native addSelect — which is why the calls should be isolated.

Sequencing. Do this after any in-flight Laravel upgrade lands, not concurrent with it, to keep the variable count at one. Adopt in read paths first; keep it out of write/transaction paths and out of anything on a latency SLO until measured. Roll out behind a repository method so that swapping the implementation for native Eloquent is a single-file change. Order of work: spike (1–2 days) → ADR → shadow tests → one endpoint → documented pattern → broader use.

Isolation pattern. Wrap every call site in a small internal helper (e.g., App\Query\ScopeSubquery::select(...)) rather than calling the package fluently throughout the codebase. This costs almost nothing and converts an unbounded dependency into a one-file dependency. It also gives a natural place to log generated SQL and enforce qualified columns.

Operational Impact

Maintenance. Low ongoing effort: a small, stable, MIT-licensed library with no configuration, no runtime services, and no migration burden. The maintenance risk is upstream, not internal — the library must track Eloquent internals. With one maintainer and no dependents, treat it as "likely fine, but budget a fork." The whole package is small enough that an internal fork with a couple of regression tests is a realistic contingency, and that contingency should be written down rather than assumed.

Support. No SLA, GitHub issues only. Internal support comes from the ADR plus a short runbook: how to inspect generated SQL, how to add a scope, what patterns are banned (unqualified columns, orderBy inside a scalar subquery, use in hot paths without EXPLAIN). Assign one owner on the platform team so questions have a destination.

Scaling. Behavior is entirely a function of the database. Correlated subqueries execute per row of the outer result set; on a large outer set without supporting indexes this degrades badly and non-linearly with data growth — and it will degrade quietly, because the application code does not change. Mitigations: restrict to queries with bounded outer result sets (paginated lists, single-record lookups); require index coverage on the correlation columns; prefer native withCount/withExists where they express the same thing; and use denormalized counters for anything on a hot path or an SLO. Capacity review should include these queries explicitly, since they will not show up as N+1 in the ORM layer and are easy to miss in profiling.

Failure modes to watch.

  • Silent wrong results from ambiguous or unqualified columns — the most dangerous mode because nothing errors.
  • Binding mismatch exceptions when combined with raw expressions or nested where groups.
  • Silent latency regression as tables grow and the correlated subquery's index stops covering.
  • Hard failure on Laravel major upgrade if the package lags builder internals.
  • Semantic mismatch when a scope containing orderBy/limit/groupBy is used in a context it was not written for.
  • Divergence from global scopes (soft deletes, tenancy) if the subquery does not inherit them the way the parent does — a correctness issue in multi-tenant apps, worth a specific test.

Ramp-up. Low. Any developer fluent in Eloquent can use it in under an hour. The non-obvious skill is reading the generated SQL and knowing when not to use it — that is a 30-minute brown-bag session plus the runbook, not a training program. Onboarding risk is that developers treat it as a performance tool rather than a DRY tool; name that explicitly in the ADR.

Recommendation. Adopt conditionally: proceed if the spike shows acceptable plans on realistic data, if call sites are isolated behind an internal helper, and if the team accepts a documented fork-or-replace fallback. Do not adopt if native withCount/withExists/addSelect already covers the duplication problem, or if the target queries sit on latency-sensitive paths where a denormalized counter is the better answer. The package solves a real but modest hygiene problem; it is not worth carrying on architectural grounds alone.

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.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log