ylsideas/cockroachdb-laravel
CockroachDB driver for Laravel that enables using CockroachDB with Eloquent, migrations, and schema builder. Connect Laravel apps to distributed SQL while keeping familiar database workflows and configuration.
ylsideas/cockroachdb-laravel adds a cockroachdb connection driver to Laravel by extending the Postgres driver with CockroachDB-specific connection, query, and schema grammars. v1.4.0 brings Laravel 11 support (PHP 8.2+), so this is the release to adopt if you're on Laravel 11.
composer require ylsideas/cockroachdb-laravel — package auto-discovery registers the service provider; no manual config/app.php entry needed (Laravel 11 uses bootstrap/providers.php only for app providers).config/database.php. Duplicate the pgsql block and change the driver:'cockroachdb' => [
'driver' => 'cockroachdb',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '26257'),
'database' => env('DB_DATABASE', 'defaultdb'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
'sslmode' => env('DB_SSLMODE', 'disable'),
],
DB_CONNECTION=cockroachdb in .env and hit php artisan migrate.First use case: stand up a single-node CockroachDB locally (docker run -p 26257:26257 -p 8080:8080 cockroachdb/cockroach:latest start-single-node --insecure), run your existing migrations, then exercise your write-heavy path. Where to look first in the source: src/Connection.php (the transaction retry wrapper), src/Schema/Grammars/* (the DDL quirks), and the README's limitations section — that file is the real API surface, because almost everything else is "Postgres, with caveats."
whereJsonContains, upsert, insertOrIgnore all work. Your existing repositories shouldn't need to change; only the connection name differs.SERIALIZABLE and surfaces SQLSTATE 40001 under contention. The package's connection class retries transaction() for you:DB::connection('cockroachdb')->transaction(function () use ($order) {
$order->decrement('stock');
$order->payments()->create([...]);
}, attempts: 5); // attempts is the retry count
Use this instead of DB::beginTransaction() / DB::commit() in any code path that can conflict with a concurrent writer. Single-statement writes are internally retried by CockroachDB and don't need the wrapper.
DB::afterCommit() works).increments()/bigIncrements() to CockroachDB's unique_rowid() default. Keep parent tables before child tables in migration order — CockroachDB validates foreign keys at creation time and won't accept forward references.DB_CONNECTION at it. Because it's multi-node-capable, it's also the only sane way to test your 40001 retry path locally — spin up a 3-node cluster, or just run concurrent workers against the single node with --parallel.RefreshDatabase works but is slower than Postgres since it runs in a transaction and can hit retry errors under parallel test workers. Prefer migrate:fresh once per run plus DatabaseTransactions on individual tests where retries are possible.cockroachdb and cockroachdb_read (a follower-read connection) and use Schema::connection(...) / on('cockroachdb_read') explicitly. Follower reads require AS OF SYSTEM TIME — raw DB::select() for those.illuminate/* constraint. If your app is still on Laravel 9/10, pin ^1.3 and don't let a composer update drag the driver forward. Check the package's composer.json before upgrading.unique_rowid() produces huge, roughly-but-not-strictly ordered 64-bit integers, interleaved across nodes. Don't rely on id ordering for "newest first"; use created_at or a uuid. Anything shipped to JavaScript will exceed Number.MAX_SAFE_INTEGER — cast IDs to strings in API resources or use HasUuids.CREATE INDEX CONCURRENTLY, exclusion constraints, and many ALTER COLUMN ... TYPE operations aren't supported. Laravel 11's column-change behavior (it no longer needs doctrine/dbal) generates DDL the CockroachDB grammar may reject — test every "modify existing column" migration against a real cluster, not SQLite.upsert() needs a matching unique index. CockroachDB's ON CONFLICT requires the conflict target to be backed by a unique constraint; a plain index will error at runtime.timestampTz() / $table->timestampTz(). CockroachDB's TIMESTAMP is timezone-naive and will silently shift values relative to Postgres expectations.public; if you set a custom schema in the connection config, prefix table names consistently or migrate will create objects in one place and queries will look in another.application_name in the connection options so statements are identifiable in SHOW QUERIES / the DB Console. EXPLAIN and crdb_internal.* are your profiler — a 40001 storm is almost always a missing index or a transaction that reads then writes a hot row, not a driver bug. Log retries with DB::listen() plus a custom retry counter to spot them.DB::extend, so you can re-register a subclassed Connection (e.g. to add metrics on retries) in your own service provider. You can also swap the query grammar at runtime with DB::connection()->setQueryGrammar() if you need custom SQL translation for a legacy query.schema:dump / squashed migrations produce a raw SQL file that Laravel replays with DB::unprepared. Verify the dumped Postgres-flavoured DDL actually loads into CockroachDB before committing it to the repo — this is the most common "works on my machine" breakage after adopting the driver.How can I help you explore Laravel packages today?