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

Dbwrapper Laravel Package

soluble/dbwrapper

Soluble DbWrapper provides a lightweight, fluent wrapper around Laravel’s database layer, simplifying common query and persistence tasks while keeping familiar Query Builder conventions. It helps reduce boilerplate for CRUD, filtering, and reusable database logic in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require soluble/dbwrapper

The package is a thin PDO wrapper — no Laravel service provider, no facade, no config file. You wire it up yourself, which is exactly the point.

Where to look first

  1. src/DbWrapper.php — the static entry point (DbWrapper::connect(...)) and the instance API.
  2. src/Connection/PdoConnection.php — the actual PDO implementation; every method you'll call day-to-day lives through here.
  3. The README example — the whole surface is small enough to read in one sitting (~10 minutes).

First use case

The canonical "hello world" is a one-off script or an artisan command hitting a database where pulling in Eloquent or even DB:: feels heavy:

use Soluble\DbWrapper\DbWrapper;

$db = DbWrapper::connect(
    'mysql:host=127.0.0.1;dbname=legacy;charset=utf8mb4',
    'user',
    'secret'
);

$rows = $db->fetchAll('SELECT id, name FROM users WHERE active = ?', [1]);

If that runs, you've seen 80% of what the package does. The remaining 20% is transactions, insert/update/delete helpers, and PSR-3 logging.

Implementation Patterns

Bind it as a singleton in a service provider

Don't call connect() on every request — the value of the wrapper is a reusable connection object.

// AppServiceProvider::register()
$this->app->singleton('legacy.db', function ($app) {
    return DbWrapper::connect(
        config('legacy.dsn'),
        config('legacy.user'),
        config('legacy.pass'),
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
    );
});

Then resolve it via app('legacy.db') in commands, jobs, and services. This is the pattern that makes the package earn its keep: a second, isolated connection to a database you don't want Eloquent managing.

Raw writes with the built-in helpers

The wrapper's insert / update / delete methods are thin sugar over prepared statements — no query builder, no model, no mass-assignment logic. That's a feature when you're writing to a legacy schema you don't control:

$db->insert('audit_log', [
    'actor'  => $userId,
    'action' => 'login',
    'at'     => date('Y-m-d H:i:s'),
]);

$db->update('users', ['last_seen' => $now], 'id = :id', ['id' => $userId]);

Transactions around multi-step work

$db->beginTransaction();
try {
    $db->execute('UPDATE accounts SET balance = balance - ? WHERE id = ?', [$amt, $from]);
    $db->execute('UPDATE accounts SET balance = balance + ? WHERE id = ?', [$amt, $to]);
    $db->commit();
} catch (\Throwable $e) {
    $db->rollBack();
    throw $e;
}

Use this for the cross-database transfer case — pulling from Eloquent's DB and writing to the legacy DB is the archetypal reason to reach for this package.

Wire in a PSR-3 logger for observability

The wrapper supports a logger (PSR-3). In a Laravel app this is the cheapest way to get query visibility from an otherwise opaque connection:

$db->setLogger(app('log'));

Now slow or failing queries land in your normal log channels instead of disappearing.

Testing: swap in SQLite

Because the public API is PDO-shaped, your test suite can hand the same DbWrapper an in-memory SQLite connection. Structure your code to take the wrapper as a constructor dependency rather than calling DbWrapper::connect() inside business logic, and tests become:

$db = DbWrapper::connect('sqlite::memory:');
$db->execute('CREATE TABLE ...');

Use alongside Eloquent, not instead of it

Keep Eloquent for your first-party domain. Reach for soluble/dbwrapper when: the connection is to a third-party/legacy DB, you don't want model classes, the query is genuinely raw, or you're writing a CLI/worker where bootstrapping Laravel's DB stack is disproportionate.

Gotchas and Tips

It's a 2019 package — audit before adopting. Last release January 2019, 7 stars, zero dependents. It's small, MIT, and unlikely to have deep bugs, but verify it runs on your PHP version (PHP 8 typed-property / deprecation issues are plausible) before betting production on it. If it doesn't fit, a ~100-line PDO subclass reproduces most of the API.

No Laravel integration ships with it. No facade, no config/dbwrapper.php, no service provider. Any Laravel-flavored convenience is on you — which is fine, but don't expect php artisan vendor:publish to do anything.

Parameter style is PDO's. Positional ? and named :name both work, but don't mix them in one statement. Named parameters in the helpers ($db->update(...)) require the WHERE clause to use the same style as the $params keys.

No query builder. Everything is a string. Concatenating user input into SQL bypasses any safety net — always pass values through the params array. The "minimalist" in the tagline means zero hand-holding here.

Set PDO options at connect time. Because there's no config layer, driver behavior (error mode, emulated prepares, fetch mode, persistent connections) is decided entirely by the $options array you pass to connect(). Set PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION explicitly — the default is silent failure, and a wrapper that returns false on error is a debugging nightmare.

Catch PDOException, not package-specific exceptions. Errors bubble up as PDO exceptions. Wrap calls in your own domain exceptions if you want them to mean anything at the application layer.

Transactions are per-wrapper-instance. If you resolve the connection twice (e.g., DbWrapper::connect() in two places), you get two PDO handles and two independent transaction scopes. Register it as a singleton, or you'll silently lose atomicity. Laravel's DB::transaction() will not cover this connection.

Extension point: the Connection interface. If you need a non-PDO driver or a decorator (retry logic, metrics), implement the package's Connection interface and hand it to DbWrapper. This is the intended seam — subclassing PdoConnection also works but couples you to its internals.

Debugging tip: when a query silently does nothing, first confirm you're on the instance with the logger attached, then check ERRMODE. Nine out of ten "the wrapper is broken" reports are a missing exception mode or a transaction started on a different instance.

Pin the version. With a single 2019 release and no maintainer activity, "soluble/dbwrapper": "^1.0" plus a lockfile is safer than a floating constraint, in case a stale branch ever gets tagged.

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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core