makinacorpus/goat-query
Goat Query is a Laravel package for composing database queries with a fluent, reusable API. It helps structure filters, joins, and conditions cleanly, making complex Eloquent queries easier to build, maintain, and test.
The package is a standalone, PDO-based query builder. It is not a Laravel package — there is no service provider, facade, config file, or Eloquent integration. Treat it as an ordinary Composer dependency that you wire up yourself.
Minimal steps:
composer require makinacorpus/goat-query (verify the exact vendor/package name and current PHP constraint; the repo is the source of truth since it has essentially no dependents).
Read the repository README.md and docs/ first — that is where the query-builder and type-converter APIs are actually documented. Do not assume an API shape from the package name.
Instantiate its session/runner over a raw \PDO handle. The most practical connection source inside a Laravel app is Laravel's own PDO:
$pdo = DB::connection()->getPdo(); // reuse Laravel's PDO
// wrap $pdo in the GOAT session/runner per the package docs
Run a first select through the builder (not raw SQL) so you exercise both the query object and the PHP↔SQL type converter in one go — insert a value with a PHP type (bool, DateTime, array) and read it back.
First use case: a reporting or ETL-style query against an existing schema, where you want explicit, type-aware escaping of PHP values into SQL without pulling in Eloquent models. That is where the "PHP to SQL and SQL to PHP type converter" actually earns its keep.
Wrap it in a container singleton. Since there is no provider, bind the GOAT session in a ServiceProvider so you control the PDO lifecycle and can mock it in tests:
$this->app->singleton(GoatSession::class, fn ($app) =>
new GoatSession($app->make('db')->connection()->getPdo())
);
Use it for the query builder, not for ORM-style work. The natural pattern is fluent select / insert / update / delete construction with placeholders and the type converter handling value formatting. Keep models in Eloquent; use GOAT only for the queries where its conversion is the point.
Lean on the type converter as a standalone helper. You can call conversion in both directions — PHP value → SQL literal, and SQL row value → PHP type — without using the builder at all. That is often the most useful day-to-day leverage: normalizing DateTime objects, booleans, nulls, and JSON-ish payloads consistently before they hit a raw statement.
Batch and bulk operations. Prefer its insert-batch / multi-row facilities over Laravel's insert() when you need per-value type conversion; that avoids manual array mapping in seeders and import jobs.
Transactions: manage them explicitly on the underlying PDO (beginTransaction / commit / rollBack), or wrap the call in Laravel's DB::transaction() and pass the same PDO in, so both layers share one transaction. Never open a second, independent connection for GOAT — split connections are the fastest way to deadlock yourself.
Keep it in its own lane. Assign GOAT to a couple of well-defined services (imports, reporting, type-sensitive pipelines) rather than letting it leak across the codebase. If a query is trivial, use DB::table(...) and stay idiomatic.
toSql()-style logging hooked into Telescope, no query-log integration. If you need observability, log the generated SQL/bindings yourself.new PDO in app code.? / :name conventions. Confirm against the docs; a wrong convention fails silently or throws opaquely.How can I help you explore Laravel packages today?