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

Couchbase Laravel Package

mpociot/couchbase

Couchbase driver for Laravel that adds Eloquent models and query builder support on top of the Couchbase PHP SDK, letting you use familiar Laravel database patterns with Couchbase documents.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm the extension before anything else. This package is a thin shim over the Couchbase PHP SDK, so the first question is not "does the package install" but "what API does my couchbase extension expose". The driver targets the legacy 2.x PECL surface:

php -m | grep -i couchbase
php --ri couchbase | head -3
php -r 'var_dump(class_exists("Couchbase\\Cluster"), class_exists("Couchbase\\N1qlQuery"));'

If you see 3.x and no N1qlQuery, stop — you are either forking the driver or not using it.

Install pinned. Zero dependents means no transitive install signal; treat resolution as unverified.

composer require mpociot/couchbase:dev-master#<commit>

Check composer.json for extra.laravel.providers. If absent, add the provider to config/app.php by hand. Then wire the connection in config/database.php:

'couchbase' => [
    'driver'   => 'couchbase',
    'host'     => env('COUCHBASE_HOST', '127.0.0.1'),
    'bucket'   => env('COUCHBASE_BUCKET', 'default'),
    'password' => env('COUCHBASE_PASSWORD', ''),
],

and a store in config/cache.php:

'couchbase' => ['driver' => 'couchbase', 'connection' => 'couchbase'],

First use case: cache, not models. The cache store is the smallest surface, exercises the connection end to end, and gives you real latency numbers immediately.

Cache::store('couchbase')->put('catalog:featured', $payload, now()->addMinutes(10));
Cache::store('couchbase')->get('catalog:featured');

Once that round-trips, do the session store via the generic cache-backed route (works regardless of which handler the package registers):

// config/session.php
'driver' => 'cache',
'store'  => 'couchbase',

Only after both work should you touch the Eloquent layer — the model base class is where the surprises live.

Where to read first. The package is small enough to read in an afternoon. In order: the service provider (DB::extend registration), the Connection class (host/credential handling, N1QL construction), and the Model base class (which parts of the relational contract it stubs). Reading those three files answers most questions this document can only warn about generically.

Implementation Patterns

Keep the driver behind a repository from commit one. The realistic outcome of adopting this package is eventually swapping it for the official couchbase/couchbase SDK. Make that a class swap.

interface CatalogRepository { public function find(string $id): ?CatalogItem; }

final class CouchbaseCatalogRepository implements CatalogRepository
{
    public function find(string $id): ?CatalogItem { return CatalogItem::find($id); }
}

Never let DB::connection('couchbase') appear in a controller or a Blade view.

Subclass the model once, not per-model.

abstract class DocumentModel extends \Mpociot\Couchbase\Eloquent\Model
{
    protected $connection = 'couchbase';
    public    $incrementing = false;
    protected $keyType = 'string';
    public    $timestamps = true;
    protected $guarded = [];
}

Every model then declares a string $table (which is the bucket/collection name — see Gotchas) and nothing else.

Own your key design; it is your primary index. There is no auto-increment and USE KEYS is the only reliably fast read path. Generate ULIDs client-side and make them readable:

$item->id = 'catalog::sku::'.$sku;   // or Str::ulid()
$item->save();

Prefix-namespaced keys let you point-read, range-scan by prefix, and reason about the bucket without an index.

Cache-aside is the highest-value day-to-day pattern here. If you are not doing document modelling, the driver earns its keep as a cache tier:

public function featured(): array
{
    return Cache::store('couchbase')->remember(
        'catalog:featured', now()->addMinutes(10),
        fn () => $this->catalog->featuredFromPrimary()
    );
}

Because the store is registered through Laravel's standard cache driver mechanism, remember, increment, forever, and lock primitives all behave as expected — assuming the driver implements the full interface, which you should confirm against the source rather than assume.

Session rollout: cache-backed first. 'driver' => 'cache', 'store' => 'couchbase' gives you a per-app cutover without depending on a custom session handler. If you need finer control (say, only a canary cohort), swap the session store per-request via middleware rather than globally; the global config change is not reversible without logging everyone out.

Treat N1QL indexes as migrations. CREATE INDEX has no artisan equivalent here. Keep .n1ql files in the repo, name them by version, and apply them out of band in the same pipeline stage that runs migrate:

CREATE INDEX idx_catalog_category ON `catalog`(category) WHERE _type = "catalog";

Review them like schema changes. Any where on an unindexed field is a full-bucket scan.

Denormalize; don't try to join. There is no whereHas and no joins. Either embed children in the parent document or do two point-reads in the repository and stitch in PHP. Write the embed-or-reference decision down per aggregate — this is the modelling work the Eloquent syntax hides.

Version your documents. Keep a schema_version field and example payloads in docs/. There is no equivalent of a migrate rollback, so a shape change is an application migration with a backfill job, not a model edit.

Testing split. Unit tests hit a fake repository. Integration tests hit a real cluster, are tagged (@group couchbase), and run only in CI — never on laptops where the extension build is unmanaged. Seed via key-value upserts of checked-in JSON fixtures, not model factories: factories assume auto-increment and Model::create() with no key will not do what you want.

foreach (glob(base_path('tests/fixtures/catalog/*.json')) as $file) {
    $doc = json_decode(file_get_contents($file), true);
    CatalogItem::unguarded(fn () => CatalogItem::create($doc)); // or explicit key set + save
}

Log the statement the driver actually sends. ->toSql() from this builder is not trustworthy. Wrap the repository layer in a decorator that logs the N1QL string plus bindings, and replay in the Couchbase web UI with EXPLAIN to confirm index usage.

Gotchas and Tips

The legacy SDK is the whole ballgame. The 2.x PECL API pins your PHP version, your extension version, and your feature set. Assume no collections ergonomics, no transactions, no scope isolation, and single-bucket defaults. If your roadmap needs any of those, this package is a dead end regardless of how good the spike looks.

Eloquent fails silently, not loudly. join, whereHas, withCount, composite keys, and DB::raw of relational SQL do not throw — they return plausible wrong results. Audit every query in a feature before porting it. Rule of thumb: any query you have not personally read is broken.

$table does not mean table. It maps to a bucket or collection depending on the driver version. Add a comment next to every $table declaration or the next developer will assume SQL semantics.

Read-your-writes costs you. Writes land in the bucket; N1QL reads go through the indexer and can lag by tens to hundreds of milliseconds. save() followed by a where('status', ...) query can miss the row you just wrote. Keep point reads (find, USE KEYS) on the write path's immediate re-read, and write an explicit read-after-write test per aggregate.

Config caching burns a debugging hour every time. php artisan config:clear after any config/*.php edit, then verify in php artisan tinker that DB::connection('couchbase') resolves and Cache::store('couchbase')->put('k','v') succeeds before blaming the driver.

Extension installation is the first blocker, not the second. PECL builds need libcouchbase headers present, and the usable base images for this SDK generation are aging. Bake and pin a Docker image on day one; do not let each developer solve the build independently.

Pagination is a trap. Length-aware pagination issues a COUNT(*) equivalent that may scan the bucket. Prefer keyset pagination (WHERE key > :last ORDER BY key LIMIT :n) and avoid exposing paginate() on large buckets until you have checked the EXPLAIN.

Mass assignment and dates. Extend the package's model with $guarded = [] only inside your own abstract base and be deliberate about it — unguarded documents with drift-prone shapes are how rogue fields appear. Also confirm $dates/casts round-trip through the JSON encoder; datetimes are stored as whatever the driver serializes, and mixed formats across documents will break whereBetween.

Extension points, in order of preference. DB::extend('couchbase', fn () => new MyConnection(...)) if the provider's registration misbehaves; query-builder macros for small ergonomic gaps (e.g. a findMany); subclassing the cache store for instrumentation. All three beat patching vendor/. If you must patch, fork immediately and pin.

You own this dependency. Zero stars, zero dependents, minimal maintenance signal. Vendor or fork it, pin the commit in composer.json, and lock PHP / Laravel / extension / server versions as a unit in CI. Assign an owner before the next Laravel security release forces the conversation.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib