megawilddaddy/simple-pdo-query-builder
A lightweight, simple PDO query builder for PHP that lets you construct and execute SQL queries through a clean, chainable API. Great for small projects that need straightforward database access without the overhead of a full ORM.
Before writing any code, read the repository in this order: README.md (usage examples and constructor signature), composer.json (required PHP version and PSR-4 namespace), then src/ (usually one or two small classes — the whole library is likely a few hundred lines). Because there is no Laravel service provider, no facade, and no config file, "getting started" is really just: install it, hand it a PDO instance, and call builder methods.
Minimal flow:
composer require megawilddaddy/simple-pdo-query-builderPDO connection yourself (DSN, user, password) — the package does not manage connections.PDO object.execute() / fetch the result and handle PDOException yourself.First realistic use case for a Laravel developer: a non-Laravel context — a CLI script, a queue worker in a legacy codebase, a small microservice, or a tool that talks to a second database where you deliberately don't want to boot Illuminate. Inside a normal Laravel app, DB::table(...) already covers this ground with better ergonomics, so the honest first use case is "I'm outside the framework but still want small, readable PDO queries."
Confirm the exact method names (select, where, get, first, insert, etc.) against the source before committing to them — with a 2022 last release and zero dependents, the README is the only contract you have.
Wrap it, don't sprinkle it. The most important pattern is to hide the builder behind your own tiny repository/interface. That gives you a seam to swap in Laravel's DB facade later and keeps the unmaintained dependency confined to one or two files.
interface UserReader {
public function findByEmail(string $email): ?array;
}
final class PdoUserReader implements UserReader {
public function __construct(private \PDO $pdo) {}
public function findByEmail(string $email): ?array {
// use the package's builder API here
// always bind $email, never interpolate
}
}
Connection management stays yours. Use a singleton or DI container binding for the PDO instance so you're not reconnecting per query. In Laravel, register it in a service provider:
$this->app->singleton(UserReader::class, fn () => new PdoUserReader(
new \PDO(config('services.legacy.dsn'), config('services.legacy.user'), config('services.legacy.pass'), [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
])
));
Typical query workflow: build → bind → execute → map. Since the library is low-level, expect to do result mapping (arrays to DTOs) yourself. Keep a thin hydrate() helper per repository rather than scattering FETCH_* logic.
Transactions are manual. Wrap multi-statement writes in $pdo->beginTransaction() / commit() / rollBack() with a try/catch. The builder won't do this for you.
Read-only external DBs are the sweet spot: point it at a reporting replica or a vendor database, keep the builder isolated, and never let it write. This is where "low-level queries with ease" actually pays off versus dragging Eloquent's model assumptions into a schema you don't own.
Testing: use SQLite in-memory (new PDO('sqlite::memory:')) for fast integration tests, or mock your UserReader interface for unit tests. Don't mock PDO itself — it's painful and brittle.
ORDER BY) that you interpolate is unescaped. Bind values; whitelist identifiers against a fixed allow-list.PDOException. Set ATTR_ERRMODE => ERRMODE_EXCEPTION explicitly and translate exceptions at your repository boundary into domain exceptions — don't let PDO errors leak into controllers.DB::listen), add a small wrapper that times and logs around execute().PDO, a DSN string, or a config array determines your whole wiring story; the README/source is the only source of truth.DB::table() — it's maintained, tested, has logging, transactions, and better binding. This package only earns its place when you're outside the framework or deliberately avoiding Illuminate.How can I help you explore Laravel packages today?