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

Truffle Laravel Package

waad/truffle

Truffle is a Laravel package that adds simple, flexible search to your Eloquent models. Define searchable fields, build expressive queries, and filter results with a clean, chainable API—no external search engine or complex setup required.

View on GitHub
Deep Wiki
Context7

Getting Started

Truffle's job is narrow: it lets Eloquent models talk to an in-memory SQLite connection instead of your real MySQL/Postgres one. Practically, that means tests (and throwaway prototype scripts) can boot a schema, run factories, and assert against models without a database server, a docker-compose service, or a CI service container.

Minimal steps

composer require waad/truffle --dev
php artisan vendor:publish --tag=truffle-config   # check README for the exact tag
  1. The service provider is auto-discovered; the published config is where the truffle connection (driver sqlite, database :memory:) is registered and where you can see the trait/hook the package exposes.
  2. Confirm the connection exists before writing a single test:
dd(DB::connection('truffle')->getDriverName()); // "sqlite"
  1. Where to look first: config/truffle.php, then the package src/ for the trait or connection resolver it ships. Read that source before the README — at 51 stars and zero dependents, the source is the documentation.

First use case — migrate one model and one test, not the whole app:

// app/Models/Invoice.php
protected $connection = 'truffle';   // or use the package trait, if provided

// tests/Feature/InvoiceTest.php
protected function setUp(): void
{
    parent::setUp();
    $this->artisan('migrate', [
        '--database' => 'truffle',
        '--path'     => 'database/migrations/truffle',
    ])->run();
}

public function test_it_totals_lines(): void
{
    $invoice = Invoice::factory()->hasLines(3)->create();
    $this->assertSame(3, $invoice->lines()->count());
}

If that green bar appears, you've validated the whole premise in one test. Then decide whether to widen adoption or stop.

Implementation Patterns

Opt-in per model, not globally. Set $connection on the handful of models you actually test in isolation. A global Model::setConnectionResolver() swap is tempting and will eventually poison a test that needed the real DB (JSON columns, generated columns, whereRaw with MySQL functions).

Schema bootstrap belongs in a base TestCase, not in every test.

abstract class TruffleTestCase extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        foreach (glob(database_path('migrations/truffle/*.php')) as $file) {
            (require $file)->up();
        }
    }

    protected function tearDown(): void
    {
        foreach (array_reverse(glob(database_path('migrations/truffle/*.php'))) as $file) {
            (require $file)->down();
        }
        parent::tearDown();
    }
}

Keeping a separate, hand-written migration set for the in-memory schema is more work than reusing your app migrations, but it lets you drop things SQLite can't express (fulltext indexes, jsonb, partial indexes) instead of fighting them. Start by reusing database/migrations with --path; fork only when a migration fails.

Pest. A beforeEach/afterEach pair or a uses(TruffleTestCase::class) at the top of the file is the cleanest distribution mechanism — it makes the in-memory decision visible in the test file rather than hidden in phpunit.xml.

Factories just work if the model's $connection is set, because factory()->create() resolves the connection from the model. Seeders do not — $this->seed() runs against the default connection; call DB::connection('truffle')->table(...) or set $connection on the seeder.

Relationships: keep the whole graph on one connection. Lazy/eager loading across connections works (separate queries), but whereHas, withCount, has(), and any join compile into a subquery on the parent's connection and will fail or silently return wrong results. If Invoice is truffle-connected, InvoiceLine must be too.

Don't mix and match in one test. A test that writes User to MySQL and Invoice to truffle is a test that can't be reasoned about, and DB::transaction() inside it will only wrap the default connection.

CLI integration, when you need it:

php artisan migrate --database=truffle   # only useful for a *file* sqlite db
php artisan db:seed --database=truffle

Note that these are only meaningful when the database is a file, not :memory: — see the gotcha below.

CI payoff. Removing the mysql:8 service container and the health-check wait loop is usually 30–90 seconds per job and one fewer flaky failure mode. That's the real business case for evaluating this package.

Gotchas and Tips

  • Every :memory: connection name is a separate database. DB::connection('truffle') and DB::connection('truffle_read') do not share storage. Use one connection name, or file::memory:?cache=shared (which reintroduces cross-test bleed). This is the single most common source of "why is my table missing?"
  • Artisan commands run out of process. $this->artisan('migrate', ['--database' => 'truffle']) does work in tests because Laravel runs it in-process; php artisan migrate --database=truffle from a terminal does not, because the PDO dies with the process. Anything that shells out — queue workers, php artisan serve, Dusk, Process::run() — will never see your in-memory data.
  • RefreshDatabase transacts the default connection only. Set protected $connectionsToTransact = ['truffle']; (and remember it migrates the default connection, which you probably don't want). For truffle, DatabaseMigrations or manual up()/down() is usually the right fit; :memory: already dies at process exit, so you may not need transaction wrapping at all.
  • config:cache freezes the connection array. If you register the connection at runtime in a service provider, cached config in CI will drop it. Publish the config file and keep the definition there.
  • SQLite is not your production engine. lockForUpdate()/sharedLock() are no-ops, LIKE is case-insensitive for ASCII, ALTER TABLE support is limited, JSON functions differ, strict typing is off. Tests that pass here can still fail in production — that's an accepted trade, but don't let truffle become the only place a query is exercised. Keep one scheduled job on the real DB.
  • Reset state between tests deliberately. If you seed once in setUpBeforeClass, ids drift and assertDatabaseCount gets brittle. Prefer a fresh up() per test; the cost is milliseconds.
  • Raw queries default to the wrong connection. DB::table('invoices'), DB::select(...), and DB::transaction(fn () => ...) all hit the default connection unless you chain ->connection('truffle'). Grep for DB:: in code under test before you trust the result.
  • Debugging: DB::connection('truffle')->listen(fn ($q) => dump($q->sql, $q->bindings)); and DB::connection('truffle')->getSchemaBuilder()->getTables() tell you almost everything. ->pretend() is useful for asserting a query compiles under SQLite's grammar before you run it.
  • Extension points worth knowing: Connection::resolverFor('truffle', ...) if you want a custom connection class; a trait on your base model for getConnectionName() if you want env-driven switching (TRUFFLE=1); and a Model::resolveConnection override in a service provider for a global, opt-out swap.
  • Treat maturity as a risk, not a blocker. 51 stars and zero dependents means no ecosystem of blog posts to rescue you. Pin an exact version, wrap everything behind your own TruffleTestCase and base trait, and keep a --database=mysql toggle in CI so ripping the package out is a one-file change rather than a refactor.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings