mouf/magic-query
Magic Query lets PHP developers describe SQL queries in JSON instead of hand-written strings, then compiles them into parameterized SQL with joins, unions and filters handled automatically. This reduces injection risk and makes complex queries easier to build, reuse and maintain across projects.
Architecture fit. mouf/magic-query is a standalone SQL string compiler: you hand it a query template containing "magic" comments (conditional fragments, optional filters, array/IN expansion) plus a parameter bag, and it emits a concrete prepared statement with a matching, correctly ordered parameter set. Architecturally it replaces programmatic SQL construction (Laravel's query builder conditionals, when() chains, manual implode of placeholders) rather than integrating with Eloquent. That makes it a plausible fit for filter-heavy read paths — admin search screens, reporting endpoints, multi-select facets, optional sort/column lists — and a poor fit for CRUD persistence, relationships, or anything you want hydrated into models.
Integration feasibility. Composer-installable and framework-agnostic; usable in Laravel via a thin wrapper around a PDO/DBAL connection pulled from config/database.php. There is no service provider, facade, or Laravel-native ergonomics, so expect to write ~100 lines of glue. The main feasibility question is dependency overlap: it targets Doctrine DBAL, and Laravel's own connector layer is separate, so you are adding a second DB abstraction to the container. That is tolerable for a bounded module and undesirable if it leaks into shared infrastructure.
Technical risk. The dominant risk is the custom SQL parser sitting on the hot path. Hand-rolled SQL parsing has a long tail — CTEs, window functions, LATERAL/APPLY, vendor-specific LIMIT … OFFSET, quoted identifiers, nested subqueries, dollar-quoted strings, function calls with commas, and ordinary /* comments */ that may collide with the magic-comment syntax. Failures are runtime exceptions rather than compile-time errors, which means they surface in production on the query shapes your tests did not cover. Secondary risk is adoption signal: 20 stars and zero listed dependents means you are effectively the QA function for the parser; the June 2025 release is a positive maintenance signal but does not substitute for field-hardening. Upside risk is real: the approach removes a whole class of injection-prone string concatenation and makes dynamic SQL reviewable as data rather than code.
Key questions.
IN expansion — syntax error, NULL rewrite, or silent no-op?Stack fit. Treat it as a query gateway, not a data layer. Introduce a single MagicQueryGateway (or per-module repository) that owns the connection handle, the query templates, and the parameter mapping; everything above it deals in DTOs and arrays, not raw SQL. Keep Eloquent for persistence and relationships, keep the query builder for simple static queries, and confine Magic Query to the conditional-heavy reads where it earns its complexity. This bounds the blast radius of a parser bug to a few endpoints and makes replacement a one-file change.
Migration path. Strangler pattern, not a rewrite. Candidate #1 is the endpoint with the ugliest existing conditional SQL — typically an admin list with 8–12 optional filters. Extract that query verbatim into a template, preserve the existing response shape, and ship behind the same route. For anything with meaningful traffic, shadow-run in staging: execute legacy and template versions against a snapshot dataset and diff result sets. Do not migrate queries that are already clean in the builder; the payoff is not there.
Compatibility. Pin the package version exactly and pin its DBAL constraint explicitly in composer.json so an unrelated composer update cannot shift the parser underneath you. Build a compatibility matrix across the engines you actually run (MySQL 8 / MariaDB / PostgreSQL / SQLite for tests) and a PHP-version check against your Laravel release. Confirm the license before distribution, especially if the parser is derived from another project.
Sequencing.
IN lists, optional ordering, one subquery) run against a disposable branch. Timebox it; the spike is where you discover whether the parser survives your real SQL.Maintenance. Small project, small maintainer pool; assume you own your usage. Pin versions, subscribe to release notifications, and read changelogs before upgrading — a parser change can alter emitted SQL without changing the API. If the package becomes load-bearing, budget for the possibility of a maintained fork; that is a real cost and should be priced into the adoption decision, not discovered later. The recent release cadence is the main reason this is viable at all.
Support. No commercial support. Internal ownership is mandatory: one named owner, a documented DSL cheat-sheet, and a lint or test that fails when a template uses unsupported constructs. On-call needs a runbook entry mapping parser exceptions to the offending template.
Scaling. Parsing is CPU work per execution; without an internal cache you are paying it on every call, which matters for high-QPS endpoints and is nearly free for admin traffic. Confirm whether a parse cache exists and, if not, wrap one keyed on template text plus the set of supplied parameter names (the shape of the query, not the values). Database-side scaling is unaffected, but large IN expansions inflate bound-parameter counts — watch placeholder limits and prepared-statement plan cache pollution when a filter accepts hundreds of IDs; chunk those explicitly. Empty-array handling is the sleeper scaling issue: it either errors under load or silently returns everything.
Failure modes.
IN list → malformed SQL or wrong cardinality./*.Ramp-up. A mid-level PHP developer can be productive in a day or two; the DSL is small. The real cost is reviewability — "clever" templating obscures intent from reviewers who have not internalized the syntax, so require the emitted SQL to be visible in tests and code review. Budget one sprint for spike + harness + first module for a team of two, and treat team unfamiliarity as the primary schedule risk rather than the library itself.
How can I help you explore Laravel packages today?