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

Eloquent Driver Laravel Package

statamic/eloquent-driver

The Statamic Eloquent Driver lets you store Statamic content—entries, terms, assets, and more—in Laravel Eloquent models instead of flat files, making content queryable in your database while keeping Statamic’s familiar APIs and workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish, but don't flip anything yet.

composer require statamic/eloquent-driver
php artisan vendor:publish --tag="statamic-eloquent-driver-config"
php artisan vendor:publish --tag="statamic-eloquent-driver-migrations"
php artisan migrate

Two published artifacts matter: config/eloquent-driver.php (the per-repository switchboard) and the migration set (entries, terms, globals, navigations, revisions, assets, users tables). Read the config file top to bottom before touching it — it is the whole API surface. Each key under repositories has a driver (file or eloquent) and usually a model you can point at your own subclass.

Look here first, in this order:

  1. config/eloquent-driver.php — which repositories exist and what their default drivers are.
  2. database/migrations/*_create_*_tables.php — the actual schema you're inheriting ownership of. Note the order, date, published, site, and data columns; you'll be reasoning about them constantly.
  3. The package's Models/ directory — these are the base classes you extend.

First use case: entries only. Leave assets, globals, navigations, and especially users on file. Entries are the highest-volume repository, the clearest performance win, and the safest to reverse.

// config/eloquent-driver.php
'repositories' => [
    'entries' => ['driver' => 'eloquent'],
    // everything else stays 'file'
],

Then import from the still-present flat files and clear the stache:

php artisan statamic:eloquent:import-entries
php artisan stache:clear
php artisan cache:clear

The importer reads from the file driver, so the source directory must still exist at import time. Verify with a count check (Entry::query()->count() vs. the number of markdown files), load a listing page, and load a dated detail page. If those three pass, you have a working install.

Implementation Patterns

Phased per-repository rollout with a soak period. The config is deliberately granular, so treat each repository as its own deployable increment: entries → terms → globals → navigations → assets → (maybe) users → never revisions unless you have a reason. Ship entries, watch it through a full editorial cycle (draft, schedule, publish, unpublish, reorder), then promote the next one. Each step is the same three commands: flip config, import, clear stache.

Keep Statamic for reading, reach for Eloquent for aggregating. The most common mistake is rewriting templates to use Eloquent. Don't. {{ collection:news }}, augmentation, and the Antlers/Blade layer keep working unchanged — that's the point of the driver. Use Eloquent where it genuinely wins:

// Reporting screen, dashboard widget, API endpoint, sitemap generator
$topTags = Term::query()
    ->where('taxonomy', 'tags')
    ->withCount('entries')
    ->orderByDesc('entries_count')
    ->limit(20)
    ->get();

// Cursor pagination over a large collection without stache overhead
foreach (Entry::query()->where('collection', 'articles')->cursor() as $entry) {
    // ...
}

// Joins against application tables — the actual reason to be here
DB::table('entries')
    ->join('orders', 'orders.article_id', '=', 'entries.id')
    ->select('entries.title', DB::raw('count(*) as conversions'))
    ->groupBy('entries.title')
    ->get();

Extend the models, never fork them. The config accepts a model per repository. Subclass, add scopes/accessors/relationships, and point the config at your class. This is also where the v5.12.3 hydration fix lands — if you subclass the entry model, you want that patch.

namespace App\Models\Statamic;

class Entry extends \Statamic\Eloquent\Entries\Entry
{
    public function scopeFeatured($query)
    {
        return $query->where('data->featured', true); // JSON column, driver-dependent
    }

    public function author()
    {
        return $this->belongsTo(\App\Models\User::class, 'created_by');
    }
}

Content promotion is now a process problem. Content is no longer in git diff. Decide explicitly, per environment, one of: (a) content is authored in production and never promoted, (b) production is the source of truth and staging is restored from a prod dump, or (c) content is seeded via seeders/factories for dev. Pattern (b) is what most teams land on because it's the least surprising. Whatever you pick, put it in the README — the next developer will otherwise migrate:fresh someone's work away.

Own your deploy script. Add php artisan migrate --force and php artisan stache:warm to the deploy pipeline. The stache warm matters: after any driver change or import, the first request otherwise pays for a cold rebuild.

Testing becomes dramatically better. This is the underrated win. RefreshDatabase plus factories means content fixtures are transactional and fast:

use RefreshDatabase;

test('news listing shows newest first', function () {
    Entry::factory()->count(3)->sequence(
        ['date' => now()->subDays(3)],
        ['date' => now()->subDays(2)],
        ['date' => now()->subDay()],
    )->create(['collection' => 'news']);

    $this->get('/news')->assertSeeInOrder([...]);
});

You'll need to write those factories yourself — the package ships models, not factories.

Rollback is one line plus a cache clear. Keep the config change isolated in its own commit so git revert is the rollback. Keep the flat-file content directory in the repo and on the server for at least the first week of production. Reverting the driver without the files present loses everything.

Gotchas and Tips

The stache is almost always the answer to "my content disappeared." After flipping a driver, running an import, or deploying, run php artisan stache:clear (and often php artisan cache:clear). Statamic caches the content index aggressively; a stale index looks indistinguishable from data loss. Before debugging schema, queries, or models, clear the stache.

The order column is authoritative as of v5.12.3. Previously the driver inferred ordering; now it reads order directly. Rows written by earlier versions or by manual INSERTs that skipped order will sort arbitrarily — often at the top or bottom, which looks like a sorting bug but is a data bug. After upgrading, spot-check every collection that supports manual ordering and backfill order where it's null. Same class of issue for Structure/nav trees: rebuild if trees render in the wrong sequence.

Timezone drift on writes is a correctness issue, not a cosmetic one. v5.12.3 fixed a timezone shift on save. Any row written on 5.12.0–5.12.2 may carry shifted date/published timestamps. Audit scheduled and date-driven content, and check config/app.php timezone against the DB session timezone — mismatches are the usual root cause and will bite again.

The users repository can lock you out of the Control Panel. A bad import leaves you with no authenticatable users and no UI to fix it. Migrate it last, if ever; do it in staging first; keep a one-line config revert and stache:clear ready; and know your php artisan tinker / DB-console recovery path before you run it.

Assets move metadata only. The assets table holds asset records and metadata; the actual files stay on disk (or in your S3 bucket). Migrating assets does not move files, and a config revert does not orphan them. This trips people up when they expect a full "assets in database" story.

Blueprints, fieldsets, collections, and taxonomies config stay as files. The driver swaps repositories, not the configuration layer. Your resources/blueprints/ and content/collections/*.yaml remain git-versioned, which is convenient — but it means "we moved content to the database" is never fully true, and per-environment blueprint drift is still possible.

Mixed-driver relationships resolve by ID and are the first suspect in a partial migration. An asset referenced by a DB-backed entry while assets are still file-based works — Statamic resolves by ID — but if the file asset's ID doesn't match what was imported, you get silent empty renders rather than errors. When something renders blank after a partial flip, check the ID space before checking your template.

Custom entry classes were slow on dated hydration until 5.12.3. If you subclassed the entry model and stayed on an earlier patch, dated listing pages were paying an unnecessary cost. Fold the patch into your normal dependency cadence and re-measure.

Rebuild derived indexes explicitly. Search indexes (php artisan statamic:search:update), the stache, and any custom caches keyed on content IDs are not automatically invalidated by an import. Add them to the migration runbook.

Delete/cascade behavior is yours now. With files, deleting an entry removed a file. With the DB, orphans in entries (revisions, relationships, term links) depend on your foreign keys and the package's delete logic. Verify what happens when you delete an entry, a term, and a collection before you let editors loose.

SQLite vs. MySQL/Postgres differ meaningfully. JSON data column querying, order/date casting, and LIKE case-sensitivity all behave differently. If you develop on SQLite and deploy to Postgres, your local tests are not validating your production queries.

Zero dependents means you are the support tier. No community answers to fall back on. Budget for reading the package source — it's small and legible, which is the mitigating factor — and pin the version in composer.json so an unattended patch doesn't land during a content freeze.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
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
cgv/localized-resource