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

Meekrodb Laravel Package

sergeytsalkov/meekrodb

MeekroDB is a lightweight PHP MySQL library that makes database access simple and safe. It offers concise query helpers, named and positional placeholders for automatic SQL injection protection, easy inserts, updates, deletes, and result handling without heavy ORM overhead.

Deep Wiki
Context7

Getting Started

MeekroDB is a standalone mysqli wrapper, not a Laravel package — there is no service provider, facade, or config file. You install it and point its static properties at a database.

composer require sergeytsalkov/meekrodb

Minimal bootstrap (e.g. app/Providers/MeekroServiceProvider.php):

use Sergeytsalkov\meekrodb\DB; // verify exact namespace in the repo's composer.json

public function register(): void
{
    $c = config('database.connections.mysql');

    DB::$host     = $c['host'];
    DB::$port     = $c['port'] ?? 3306;
    DB::$user     = $c['username'];
    DB::$password = $c['password'];
    DB::$dbName   = $c['database'];
    DB::$encoding = $c['charset'] ?? 'utf8mb4';

    DB::$throw_exception_on_error = true; // never ship the default HTML error output
}

Look first at the README's placeholder table (%s, %i, %d, %ss, %ls, %li, %n) and the helper method list — those two things define 90% of daily usage.

First real use case — a reporting/read query where Eloquent is overkill:

$user = DB::queryFirstRow(
    "SELECT id, email FROM users WHERE email=%s_email",
    ['email' => $email]
);

$count = DB::queryFirstField(
    "SELECT COUNT(*) FROM orders WHERE created_at >= %s_since AND status=%s_status",
    ['since' => $since, 'status' => 'paid']
);

DB::insert('audit_log', ['user_id' => $user['id'], 'action' => 'login']);
$id = DB::insertId();

That's the whole loop: insert/update/delete, queryFirstRow, queryFirstField, query (returns array of assoc rows).

Implementation Patterns

Bind config once, in a provider or a small wrapper class. Reading config('database.connections.mysql') keeps credentials in one place and respects .env. Register the provider early so the static config is set before any job or command runs.

Keep MeekroDB behind a thin repository. Because it is static, it is hard to fake in tests. Wrap it:

final class ReportQueries
{
    public function dailyTotals(string $since): array
    {
        return DB::query(
            "SELECT DATE(created_at) d, SUM(total) total
               FROM orders WHERE created_at >= %s_since
              GROUP BY d ORDER BY d",
            ['since' => $since]
        );
    }
}

Now tests can mock ReportQueries without touching mysqli.

Use list placeholders for IN clauses — this is where MeekroDB saves the most boilerplate:

DB::query("SELECT * FROM users WHERE id IN %li", $ids);        // [1,2,3] -> (1,2,3)
DB::query("SELECT * FROM users WHERE role IN %ls", $roles);    // ['a','b'] -> ('a','b')

Writes:

DB::update('users', ['last_login' => DB::sqleval('NOW()')], 'id=%i', $id);
DB::delete('sessions', 'expires_at < %s_cutoff', ['cutoff' => $cutoff]);
DB::affectedRows();

DB::sqleval() is the documented escape hatch for raw SQL expressions inside insert/update arrays (NOW(), COUNT(*)+1, etc.).

Transactions:

DB::startTransaction();
try {
    DB::insert('orders', $order);
    DB::update('stock', ['qty' => DB::sqleval('qty-1')], 'sku=%s', $sku);
    DB::commit();
} catch (\Throwable $e) {
    DB::rollback();
    throw $e;
}

Wrap in try/finally if you also want to surface committed state on later failures.

Error handling in Laravel. Turn on DB::$throw_exception_on_error and either catch MeekroDBException at the boundary or set DB::$error_handler to a closure that throws a Laravel-friendly exception (the handler receives ['error', 'errno', 'query']):

DB::$error_handler = function (array $p) {
    throw new \Illuminate\Database\QueryException(
        $p['query'], $p['query'], [], new \Exception($p['error'], $p['errno'])
    );
};

That single conversion gets you working logs, report(), and consistent 500 responses.

Where it fits day-to-day in a Laravel app: data imports, admin/reporting screens, legacy bridge code being ported table-by-table, artisan commands, and one-off maintenance scripts. For anything that mutates domain state inside Laravel's normal request lifecycle, the query builder still wins.

Gotchas and Tips

  • It is a second, independent mysqli connection. It does not share Laravel's connection, transaction, or query log. Never mix DB:: calls with DB::transaction()/Eloquent inside the same unit of work — you can deadlock against yourself or silently commit half a transaction. Pick one connection per logical operation.
  • No Telescope, no DB::listen, no slow-query log. If you rely on those, wrap the repository and log durations yourself. DB::$last_query (or the query log, if enabled) is your only in-library introspection.
  • %s already quotes the value. Writing WHERE name='%s' produces doubled quotes. Use %ss when you need an escaped-but-unquoted value inside an existing quoted context, and %s when you want MeekroDB to supply the quotes.
  • Named placeholders must be exact. Keys in the array are matched to %s_name; a missing key raises an error rather than injecting NULL. Mixing positional args and a named array in one call is not supported.
  • Guard empty lists. %li / %ls with an empty array produces invalid SQL. Short-circuit if (empty($ids)) return []; before building the query.
  • %n for NULL, not %s. A string 'NULL' is not NULL, and passing PHP null through %s will quote it. Reach for %n explicitly.
  • Default error behavior prints HTML/var_dump. If you forget throw_exception_on_error, a failed query inside a JSON API returns garbage markup with a 200. Set it in the provider, always.
  • MySQL-only. No SQLite fallback, so phpunit.xml test DBs must be MySQL. Either provision one in CI or keep MeekroDB behind interfaces and fake it.
  • No schema builder or migrations. Keep Laravel migrations as the source of truth for DDL; use MeekroDB only for DML/queries.
  • Charset must be set explicitly. DB::$encoding = 'utf8mb4' — omitting it can mangle emoji on older servers.
  • Long-running queue workers can go stale. The connection persists for the process; on "server has gone away" errors, catch, call DB::disconnect(), and retry once.
  • Extension points are the statics: DB::$param_char (change the % sigil), DB::$error_handler, DB::$nonsql_error_handler (connect failures), and DB::$throw_exception_on_error. There is no event system — anything fancier goes in your wrapper class.
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels