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

Database Laravel Package

webman/database

Database component for webman that integrates Laravel's Illuminate database layer. It offers the query builder, Eloquent ORM, schema builder, migrations, and pagination, with connection pooling for high-performance webman apps. Works with MySQL, PostgreSQL, SQLite, and SQL Server.

View on GitHub
Deep Wiki
Context7

Getting Started

webman/database is the official database layer for webman: a thin integration of illuminate/database (Laravel's query builder + Eloquent) wired into the webman/Workerman process model. There is no Artisan, no service container of your own, and no .env magic unless you add it — it's a config array plus two classes you'll use constantly: support\Db and support\Model.

As of v2.1.10, the package accepts illuminate/database ^10.0 || ^11.0 || ^12.0 || ^13.0 — i.e. it now spans Laravel 10 through 13's DB layer. Practically: the query builder/Eloquent API you're used to is stable across that range, but the minor Illuminate version your lockfile resolves is what your docs/IDE autocomplete should be based on. Check with composer show illuminate/database after install.

Minimal steps:

composer require -W webman/database

Then make sure config/database.php exists (copy the default from vendor/webman/database/src/config/database.php or via the plugin installer, php webman plugin webman/database, depending on the version you pull). It is a plain PHP array:

return [
    'default' => 'mysql',
    'connections' => [
        'mysql' => [
            'driver'   => 'mysql',
            'host'     => getenv('DB_HOST') ?: '127.0.0.1',
            'port'     => 3306,
            'database' => 'webman',
            'username' => 'root',
            'password' => '',
            'charset'  => 'utf8mb4',
            'prefix'   => '',
        ],
    ],
];

Restart workers after editing it (php start.php restart) — config is read at process boot, not per request.

First use case, in a controller:

use support\Db;

$users = Db::table('users')
    ->where('active', 1)
    ->orderByDesc('id')
    ->limit(20)
    ->get();

First model:

namespace app\model;

use support\Model;

class User extends Model
{
    protected $table = 'users';
    protected $fillable = ['name', 'email'];
}

Where to look first: config/database.php, the support\Db and support\Model stubs in vendor/webman/database/src/, then treat the upstream illuminate/database docs as your real reference — everything there applies, pinned to whichever major your composer.lock resolved (10, 11, 12, or 13).

Ignore the GitHub star/dependent counts here. This package is effectively installed for you by webman's project skeleton and is the default DB layer of a large ecosystem; its maturity signal is upstream Illuminate, not the repo's star count. The v2.1.10 constraint bump is a good example of this: the release is one line of composer.json, and its value is entirely "you can now track current Illuminate."

Implementation Patterns

Keep controllers thin, but don't over-abstract. A repository layer is fine, but the practical win in webman is avoiding N+1 queries because DB calls block the worker's event loop. Eager load aggressively:

$orders = Order::with(['user:id,name', 'items'])->where('status', 'paid')->paginate(30);

Query builder for anything bulk or report-shaped. Eloquent hydration is expensive; skip it:

Db::table('events')->insert($rows);          // batch insert, one round trip
Db::table('stats')->upsert($rows, ['day'], ['count']);
Db::table('orders')->where('created_at', '<', $cutoff)->chunkById(1000, function ($chunk) { /* archive */ });

chunkById rather than chunk — offset pagination degrades and can skip rows on a mutating table. (Older API names like chunkById vs lazyById behave the same across the supported 10–13 range, so no porting needed if you upgrade.)

Transactions wrap the closure, always. Manual beginTransaction in a long-lived worker is the source of most "why is this request stuck in a transaction" bugs:

Db::transaction(function () use ($data) {
    $order = Order::create($data);
    Db::table('order_items')->insert($items);
    return $order;
}, 3); // 3 attempts, retries on deadlock

Cross-connection work is separate: Db::connection('reporting')->transaction(...) is not part of the outer transaction. No distributed transactions here.

Pagination works out of the box because the package registers the currentPath/currentPage resolvers from the current webman request:

return view('user/index', ['users' => Db::table('users')->paginate(20)]);

with {{ $users->links() }} in the Blade template.

Observe queries from a bootstrap class, not from controllers:

// app/bootstrap/DbLog.php
namespace app\bootstrap;

use support\Db;
use support\Log;

class DbLog implements \Webman\Bootstrap
{
    public static function start($worker)
    {
        Db::listen(function ($q) {
            if ($q->time > 200) {
                Log::warning('slow sql', ['sql' => $q->sql, 'bindings' => $q->bindings, 'ms' => $q->time]);
            }
        });
    }
}

Register it in config/bootstrap.php. This runs once per worker process and covers every query in it.

Model events and observers (creating, saved, deleting) work as in Laravel and are the cleanest place for audit trails, slugs, and cache invalidation. Register them in a bootstrap class so they aren't re-registered per request.

Multi-database: define extra connections and pin models with protected $connection = 'reporting'; or per-query User::on('reporting')->find(1).

Migrations/seeders: there's no artisan migrate. Either add webman/console and write a command that drives Illuminate\Database\Migrations\Migrator/DatabaseMigrationRepository, or run migrations from a standalone script that boots the DB manager first. Most teams do the former once and forget about it. Note the migrator APIs shifted slightly between Illuminate 10 and later majors — if you pin webman/console for a while, re-read the migrator signature after any Illuminate bump rather than assuming.

Coroutines/async: standard webman handles one request at a time per worker, so a single shared PDO per process is correct and cheap. If you introduce coroutines (webman/coroutine, Swoole-style), that shared connection is no longer safe — you need per-coroutine connections or a pool.

Gotchas and Tips

  • The PDO handle lives as long as the worker. Session variables, SET time_zone, temp tables, LAST_INSERT_ID, and — critically — an open transaction will leak into the next request handled by that worker. Never leave a transaction open on an exception path; use Db::transaction() or try { } finally { Db::rollBack(); }.
  • SQLSTATE[HY000] 2006 MySQL server has gone away is the classic long-running-process failure: MySQL's wait_timeout kills idle connections overnight. Mitigate by raising wait_timeout, catching QueryException and retrying once on a fresh connection, or proactively dropping connections on a Workerman\Timer / before long-idle paths (Db::connection()->disconnect() then reconnect). Queue consumers and cron workers are the most exposed.
  • Db::enableQueryLog() is a memory leak in a worker. It accumulates forever. Use Db::listen() with sampling or a time threshold instead, and Db::flushQueryLog() if you must.
  • A slow query stalls the whole worker, not just one request — there's no thread to fall back on. Watch the slow-query listener, and size max_conn (workers × connections per worker) against MySQL's max_connections.
  • Pagination views. illuminate/pagination resolves its default view against a view factory that webman's Blade integration doesn't fully populate. If links() errors, set Paginator::defaultView(...)/defaultSimpleView(...) to your own compiled view, or render markup manually. Custom page params require overriding Paginator::currentPageResolver. The default view markup has changed between Bootstrap 4/5 presets across Illuminate majors — if links() output changes after an Illuminate bump, that's why.
  • Models need a bootstrapped process. Model::getConnectionResolver() is null in a bare CLI script. If you write standalone console scripts, set the resolver manually or stick to support\Db.
  • Strict SQL mode bites. The default MySQL config inherits server strictness, so groupBy with non-aggregated columns fails where it used to pass. Set 'strict' => false deliberately, or fix the query; don't disable it globally and forget.
  • Timezone drift: PHP and MySQL can disagree. Pick UTC everywhere, set 'timezone' => '+00:00' on the connection if you need MySQL-side NOW() to agree with Carbon, and keep $dateFormat consistent on models.
  • Never var_dump/dd in a worker — it writes into the HTTP response stream or the console that's also serving traffic. Log via support\Log.
  • Illuminate version pinning. Since v2.1.10, webman/database allows illuminate/database ^10 || ^11 || ^12 || ^13, so the package itself is no longer the bottleneck when you upgrade Laravel-adjacent libraries — but other plugins may still pin an older major, and Composer will silently hold you back to the lowest common denominator. After any upgrade, run composer why illuminate/database and composer show illuminate/database to confirm which major you actually got, because your code's available methods depend on it. Use composer require -W when adding DB-adjacent packages. Config changes and code changes both require php start.php reload or restart — editing files does not hot-reload workers.
  • Upgrading the Illuminate major is not a no-op. The ^13.0 allowance doesn't mean your models are 13-compatible: deprecations in casts, Model::$dates, and connection/grammar internals accumulate across majors. Bump the constraint in a branch, run your test suite plus a smoke request against each DB connection, and check Db::listen() output for changed SQL (identifier quoting, insertOrIgnore syntax) before shipping.
  • Extension points: Db::extend('driver', fn() => ...) for custom drivers, a custom Connection subclass, and Db::listen() for instrumentation. That's usually enough — you rarely need to fork the package.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console