rgalstyan/laravel-aggregated-queries
Laravel Aggregated Queries helps you combine multiple database queries into one aggregated call, reducing round trips and simplifying complex reporting. It keeps query builder workflows familiar while making data fetching more efficient and code easier to maintain for dashboards and reports.
Architecture fit. The package targets a narrow but real inefficiency in Eloquent: eager loading belongsTo/hasOne/hasMany executes one additional query per relation (1 + N_relations round trips) and then hydrates every related row into a model in PHP. The package replaces that with SQL-side JSON aggregation (JSON_ARRAYAGG on MySQL, json_agg/json_build_object on Postgres, json_group_array on SQLite 3.38+) so a parent row arrives with its relations pre-assembled as JSON, hydrated once. Architecturally this is a read-side optimization layered onto the query builder, not a persistence change — which is the right shape for a Laravel package. The critical unknown is how it layers on: whether it hooks Model::with() transparently (high value, high breakage risk) or introduces an opt-in builder macro / dedicated query class (lower value, lower risk). The latter is far more likely given the maturity signals, and it materially changes the integration cost.
Integration feasibility. Composer-installable, MIT, Laravel-native dependencies. Feasible to spike in under a day. The gating factor is SQL dialect coverage — MySQL 5.7+/8.0, MariaDB, PostgreSQL, SQLite, and SQL Server (where aggregation via FOR JSON PATH is awkward) will not behave uniformly, and any dialect gap is a hard runtime failure, not a graceful degradation. Secondary gates: whether the package supports nested relations, constrained relations (with(['comments' => fn($q) => ...])), belongsToMany with pivot columns, polymorphic relations, and soft-deleted/scoped models.
Technical risk. This is the highest-risk area, and it is concentrated in the aggregation model itself:
hasMany relations in a single GROUP BY produces N×M rows before aggregation; correctness survives, cost does not. Any implementation must aggregate each relation in a correlated subquery/CTE, not a single flat join. Verify this explicitly — it is the single most common failure of this technique.$appends, date serialization, and hidden/visible rules. Silent divergence here corrupts API contracts without throwing.limit() applies to the aggregate's input, not the parent set; naive composition returns wrong page sizes. Correct implementations need a subquery-then-join pattern.withCount, withSum, cursor pagination, chunkById, and global scopes is unproven.Key questions for the maintainer / spike. Which Laravel and PHP versions are in the CI matrix? Which DB drivers are tested, and is MariaDB distinguished from MySQL? Does it preserve model events-free hydration semantics (casts, accessors, appends)? Does it support nested and constrained relations? What are the published benchmarks versus with() across parent-count and relation-cardinality axes? Is it read-only by design? What is the query-construction API — trait, macro, or standalone builder?
Stack fit. Best fit is a Laravel 10/11/12 application on PHP 8.1+ with MySQL 8 or PostgreSQL, serving read-heavy JSON API endpoints where the response shape already mirrors the Eloquent relation tree. Poor fit for SQL Server shops, apps on MySQL 5.6, or codebases that rely heavily on model events and accessor side effects during serialization.
Migration path. Do not adopt wholesale. Identify one endpoint satisfying the ideal profile — large parent set (hundreds+), two or more hasMany relations, no write path, stable response contract — and run the package against it behind a feature flag or a repository/query-object boundary. Diff the JSON output byte-for-byte against the existing with() implementation. Only after equivalence and a measured latency/query-count win should the pattern propagate.
Compatibility. Verify composer.json constraints against the app's Laravel and PHP versions before anything else; a single major-version gap ends the evaluation. Because the package has zero dependents on Packagist, there is no ecosystem evidence that it tracks Laravel majors promptly — assume you will be the one discovering upgrade breakage. Budget for contract tests that assert response shape on every Laravel and package upgrade.
Sequencing.
hasMany without row multiplication, and inventory dialect coverage.with() vs. package, across parent counts (10/1k/100k) and relation counts (1/2/3).Maintenance. Single maintainer, 10 stars, zero dependents, last release December 2025 — the package is technically alive but has essentially no production validation surface. MIT licensing and a likely small code footprint (query construction plus a hydrator) make it cheap to vendor or fork if upstream stalls. Treat it as a utility you may own, not a dependency you can lean on.
Support. No community, no Stack Overflow corpus, no published issue backlog of substance. Internal debugging will require comfort with dialect-specific JSON aggregation — a real but learnable skill, and one that will concentrate in whichever engineer owns the query layer. Plan for a named owner.
Scaling. Wins accrue from eliminated round trips and reduced Eloquent hydration overhead; they are largest at high parent counts with modest relation cardinality, and negligible on small result sets. Costs shift to the database: GROUP BY plus JSON serialization increases DB CPU, sort/temp-file pressure, and per-query memory. Watch the payload — a single aggregated row can be far larger than the equivalent flat result, and PHP's json_decode on a wide result set can offset the hydration savings. Load-test at realistic cardinality rather than trusting the README.
Failure modes.
limit() interacting with GROUP BY.Ramp-up. Low absolute cost — one engineer, one to two days to evaluate and roughly a week to productionize a single endpoint, assuming SQL JSON aggregation familiarity. The residual risk is ongoing stewardship rather than initial learning.
Recommendation. The underlying technique is sound and worth adopting selectively; the package as a dependency is not yet de-risked. Run a time-boxed spike, and if the measured win on a real endpoint exceeds roughly 30% latency or meaningfully cuts query volume, vendor the aggregation logic internally rather than depend on a 10-star, single-maintainer package for a load-bearing read path. If the spike shows marginal gains, keep with() — Eloquent's eager loading is already one-query-per-relation and the ceiling on this optimization is lower than it first appears.
How can I help you explore Laravel packages today?