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

Laravel Package Quick Demo Laravel Package

zpmlabs/laravel-package-quick-demo

zpmlabs/laravel-package-quick-demo is a compact Laravel package example for quickly understanding a minimal reusable package setup. It serves as a starter reference for package structure, service provider registration, and Composer integration in Laravel applications, ideal for developers prototypin...

View on GitHub
Deep Wiki
Context7

Getting Started

The package solves one problem: shipping a runnable demo with a Laravel package without touching the host app's .env, config/database.php, or routes/web.php. It does this by registering a DemoDefinition per demo key, spinning up an isolated SQLite connection in database/quick-demos/{name}.sqlite, running migrations/seeders against that connection, and mounting routes/views/assets/storage/config under prefixed namespaces.

Minimal steps:

  1. composer require zpmlabs/laravel-package-quick-demo:^0.1
  2. Create examples/ in your package with only what you need:
    examples/
      database/demo.sqlite.stub        # empty file is fine: touch it
      migrations/2026_01_01_000001_create_demo_posts_table.php
      routes/demo.php
      views/index.blade.php
    
  3. Register the demo in your package's boot():
    use ZPMLabs\LaravelPackageQuickDemo\Facades\QuickDemo;
    use ZPMLabs\LaravelPackageQuickDemo\Support\DemoDefinition;
    
    QuickDemo::register(
        DemoDefinition::make(
            key: 'blog-demo',
            name: 'Blog Demo',
            databaseName: 'blog_demo',
            databaseStubPath: __DIR__.'/../examples/database/demo.sqlite.stub',
            migrationsPath: __DIR__.'/../examples/migrations',
            seeders: [\Vendor\BlogPackage\Database\Seeders\BlogDemoSeeder::class],
            routesPath: __DIR__.'/../examples/routes/demo.php',
            viewsPath: __DIR__.'/../examples/views',
        )
    );
    
  4. Run in a test app:
    php artisan quick-demo:list
    php artisan quick-demo:show blog-demo
    php artisan quick-demo:install blog-demo
    php artisan serve
    
  5. Open http://localhost:8000/quick-demo/blog-demo.

Where to look first:

  • quick-demo:show {key} — prints every resolved path, connection name, and prefix. This is your ground truth; if a path is wrong here, the install will misbehave.
  • QuickDemoContext — the object handed to lifecycle hooks. Fields like $context->databasePath, $context->connectionName, $context->routePrefix tell you exactly what the package decided.

First use case: a read-only demo of your package's public surface (list + show pages over seeded data), no auth, no writes. Start database-only (stub + migrations), add routes/views once data renders.

Implementation Patterns

Isolation is the whole contract. Every layer must be scoped to the demo key. The three places people break isolation:

// Migration — override getConnection()
public function getConnection(): string
{
    return QuickDemo::connectionName('blog-demo');
}

// Seeder — target the demo connection explicitly
DB::connection(QuickDemo::connectionName('blog-demo'))
    ->table('demo_posts')->insert([...]);

// Model — override getConnectionName()
public function getConnectionName(): string
{
    return QuickDemo::connectionName('blog-demo');
}

Naming conventions that actually matter. The demo key (blog-demo) becomes a route prefix, a name prefix, and a view namespace. Prefixing is not cosmetic — it prevents collisions with a host app that may already own posts/index or /demo. Use package-specific keys (blog-demo), database names (blog_demo), and table names (demo_posts). Generic keys (demo, test, example) are called out as hard "don't"s because they collide across packages in the same host app.

Route/view namespacing is automatic. Your examples/routes/demo.php should define unprefixed routes:

Route::get('/', [BlogDemoController::class, 'index'])->name('index');

The package mounts them under /quick-demo/blog-demo and names them quick-demo.blog_demo.index. Views resolve via quick-demo-blog-demo::index.

Publishing destinations are fixed by the package — use these in Blade/controllers instead of inventing your own:

  • Assets → public/vendor/quick-demos/blog-demo → {{ asset('vendor/quick-demos/blog-demo/demo.css') }}
  • Storage → storage/app/quick-demos/blog-demo → storage_path('app/quick-demos/blog-demo/images/demo-cover.jpg')
  • Config → config/quick-demos/blog-demo.php

Omit paths you don't have. DemoDefinition::make() accepts optional paths; passing a non-existent path (e.g. configPath when you have no config file) is a classic source of install failures. Register only what exists.

Use hooks sparingly. beforeInstall/afterInstall (context: QuickDemoContext) are the escape hatch for things the standard publish pipeline can't do — e.g., generating derived demo assets, calling an external service, or verifying a dependency. For anything covered by migrations/seeders/routes/views/assets/storage/config, don't reach for a hook.

Idempotent re-install. php artisan quick-demo:install blog-demo --fresh --force is the dev loop. --fresh resets the SQLite database; --force overwrites published files. Without these, a re-install won't clobber your demo after you change a migration or seeder.

Multi-demo packages. Register multiple QuickDemo::register() calls with distinct keys — the connection is registered per-demo (quick_demo_blog_demo, quick_demo_orders_demo), so demos are independent. Each has its own SQLite file, route prefix, and view namespace.

Gotchas and Tips

Schema::create() without ->connection() is the single most common mistake. The migration runs but on the default DB, creating fake production tables and leaving the demo blank. Always Schema::connection($this->getConnection()). Same trap with DB::table() in seeders — must use DB::connection(...).

Models not honoring the connection. If getConnectionName() is omitted (or a trait/relationship bypasses it), queries hit the default connection. Watch for Model::with() eager loads and pivot models in demo data — they need the same override.

Route name prefix collision on the host app. Route names use quick-demo.{dbname}.{name}. If you use a generic key like demo, a second package with the same key will collide on route names and the second install will fail or overwrite route names. Pick a package-specific key.

Config publishing requires the configPath to exist and be a file. A directory or missing file typically produces a confusing copy error. Same for databaseStubPath — the stub must exist; an empty file is fine, but missing is not.

Don't hand-edit the generated database or connection config. They live in database/quick-demos/ and the runtime connection config (registered via the package) — the demo must remain re-installable with --fresh. If you need seeded state, put it in a seeder, not a commit of the .sqlite file.

--force is required after edits. During development, edits to migrations/seeders won't apply on plain quick-demo:install — the published files already exist and the DB already has tables. Use --fresh --force.

Debugging checklist when a demo installs but renders empty/wrong:

  1. php artisan quick-demo:show {key} — confirm resolved paths and connection name.
  2. Inspect storage/logs/laravel.log for SQL "no such table" — usually a missing getConnection() override.
  3. Check database/quick-demos/{dbname}.sqlite exists and is non-empty after install.
  4. Check php artisan route:list | grep quick-demo — confirm the route prefix.
  5. Verify config/database.php and .env are unchanged (isolation contract).

Validation the package expects after you implement:

php artisan quick-demo:list
php artisan quick-demo:show blog-demo
php artisan quick-demo:install blog-demo --fresh --force
php artisan route:list

Confirm demo appears, DB exists, migrations/seeders ran on the demo connection, no demo tables leaked into the default DB, routes are under /quick-demo/{key}, assets/storage/config copied where expected, and host .env/config/database.php/routes/web.php untouched.

Extension point summary: DemoDefinition fields (afterInstall, beforeInstall, seeders, paths) and QuickDemoContext (definition, databasePath, connectionName, routePrefix, routeNamePrefix, viewNamespace, assetsDestination, storageDestination, configDestination, options). Hooks receive the context and can log/validate/verify — but should stay stateless and idempotent since --fresh re-runs them.

Keep naming in English — the package's own convention treats non-English identifiers/comments as a review defect in package additions.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource