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

Workbench Laravel Package

illuminate/workbench

Illuminate Workbench gives Laravel developers a dedicated local workspace for building packages. Scaffold new packages, wire up service providers and autoloading, run tests, and iterate on reusable components quickly with Artisan commands before publishing them to Packagist.

View on GitHub
Deep Wiki
Context7

Getting Started

illuminate/workbench is not a package you install — it is the read-only subtree split of the Illuminate\Workbench component that already ships inside laravel/framework. The 31 stars and the "[READ ONLY]" description are the tell: issues and PRs belong in laravel/framework, and the split repo exists only so Composer/libraries can resolve the component in isolation. Treat it as documentation-of-record for a tool you already have.

Minimal steps

  1. Confirm you actually have it. It landed in the Laravel 10 line and is standard in 11/12:
    composer show laravel/framework | grep -i version
    php artisan list workbench
    
    If workbench:* commands are missing, your framework is too old — upgrading Laravel is the fix, not composer require illuminate/workbench.
  2. Scaffold your first package from the host app root:
    php artisan workbench:create --help   # see prompts/flags for your version
    php artisan workbench:create         # answer vendor + package name prompts
    
  3. Let Composer wire it up. The command edits the root composer.json (a path repository under packages/ plus a require entry). If your version does not run it for you:
    composer update
    
  4. Inspect what you got and where it lives:
    php artisan workbench:list
    ls packages/<vendor>/<name>
    
  5. Boot the host app and hit a route from the package:
    php artisan serve
    

Where to look first

  • packages/<vendor>/<name>/src/ — your provider and classes.
  • packages/<vendor>/<name>/composer.json — the extra.laravel.providers block is what makes auto-discovery work in the host app.
  • Root composer.json — the generated repositories entry is the contract between app and package. If package code “isn’t there”, this is the first place to look.
  • The framework source (vendor/laravel/framework/src/Illuminate/Workbench) rather than this repo — it is exactly the same code, but you get the surrounding console kernel wiring.

First useful use case: extract a Blade component, validation rule, or Artisan command you keep copy-pasting between projects into packages/acme/utilities, iterate against a real app (not a mock), then publish it as a standalone repo with git subtree split when it stabilises. Workbench’s whole value proposition is “develop a package with the full app available at runtime.”

Implementation Patterns

The live-symlink loop (the default and the best one). Composer path repositories symlink by default, so editing packages/acme/utilities/src/Foo.php is immediately visible to the host app with zero composer update. Keep it that way; only switch to "options": { "symlink": false } if you specifically need to simulate a dist install (e.g., verifying .gitattributes export-ignore behaviour).

Provider registration and auto-discovery. Declare the provider in the package’s composer.json so the host app boots it without touching config/app.php:

{
  "extra": {
    "laravel": {
      "providers": ["Acme\\Utilities\\UtilitiesServiceProvider"]
    }
  }
}

Note that this is read by the host app’s package manifest — after adding it you must composer update (or at minimum php artisan package:discover) before the provider runs.

Wire routes, views, migrations, and config in boot()/register() rather than relying on the host app. The package should be self-contained:

public function boot(): void
{
    $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
    $this->loadViewsFrom(__DIR__.'/../resources/views', 'utilities');
    $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
    $this->loadTranslationsFrom(__DIR__.'/../lang', 'utilities');

    $this->publishes([
        __DIR__.'/../config/utilities.php' => config_path('utilities.php'),
    ], 'utilities-config');
}

Group routes with the web middleware explicitly — Workbench does not add it for you, and ungrouped routes silently miss sessions/CSRF.

Migrations during development. php artisan migrate from the app root picks up loadMigrationsFrom paths, which is usually what you want while iterating. For ad-hoc runs outside the provider:

php artisan migrate --path=packages/acme/utilities/database/migrations

Tests live with the package. Use Orchestra Testbench so the package has its own phpunit.xml and does not depend on the host app:

cd packages/acme/utilities && composer test

This is the single most important pattern: the host app is your manual test rig, Testbench is your automated one. Without this, workbench:delete destroys your only test suite.

Git strategy — pick one deliberately.

  • Monorepo: commit packages/acme/utilities alongside the app; split later. Easiest, but the package inherits the app’s git history.
  • Separate repo + submodule or path repo pointing at a sibling clone: cleaner for multi-project packages. Point the path repository url at ../utilities instead of packages/utilities.
  • Throwaway: gitignore packages/, delete when done.

Multi-package workspaces. Workbench happily manages several packages under packages/ at once (workbench:list enumerates them). Keep each package’s composer.json autoload PSR-4 maps distinct (Acme\Utilities\ vs Acme\Billing\) — namespace collisions are the most common cause of “class not found” after adding a second package.

Consumer-side integration. Once the package is public, the same code works unchanged as a normal composer require acme/utilities dependency. The only thing that changes is that the path repository disappear — so verify early that nothing in the package relies on being symlinked (e.g., __DIR__ assumptions beyond the package root, or hardcoded packages/ paths in tests).

Gotchas and Tips

The read-only repo trap. Do not open issues or PRs at illuminate/workbench — they will be closed or ignored. Reproduce bugs in a Laravel app, then report against laravel/framework. Likewise, never add illuminate/workbench to a require block: you will either get a version-conflict with laravel/framework or a duplicate copy of the component.

Version drift between the split and your framework. illuminate/workbench is tagged in lockstep with the framework, but the split can lag on Packagist. If php artisan workbench:* behaviours differ from what you read in this repo, trust vendor/laravel/framework/src/Illuminate/Workbench — that is the code actually executing.

Path repository resolution. Two related pitfalls:

  • Version constraints must be permissive. "acme/utilities": "^1.0" against a path repo with no tags fails resolution. Use "*@dev" during development.
  • If acme/utilities also exists on Packagist, Composer may resolve the published copy instead of your working tree. Force it with an explicit @dev constraint or a "canonical": true path repository entry. Symptom: your edits have no effect and composer show acme/utilities points at vendor/ instead of packages/.

Autoload edits require a Composer run. Adding a class to an existing PSR-4 prefix just works (symlinked dir, Composer scans on demand). Adding a new prefix, a files autoload, or a new extra.laravel.providers entry does not — run composer update acme/utilities or composer dump-autoload. This is the number-one “why is my provider not firing” cause.

Caches lie. After changing routes, config, or a service provider, always:

php artisan optimize:clear

In dev, consider setting APP_ENV=local and never running config:cache while iterating on a package — a cached config snapshot hides your package’s config merge entirely.

workbench:delete is incomplete on purpose. It removes the directory, but historically leaves the root composer.json repositories and require entries behind. Clean those manually and run composer update, otherwise Composer will hard-fail with “could not find package.”

Windows symlinks. Composer’s path repo symlinking needs Developer Mode or an elevated shell. Without it Composer falls back to mirroring, and your “live” edits silently stop propagating. If in doubt, check whether vendor/acme/utilities is a junction.

Don’t edit vendor/acme/utilities. It should be a symlink to packages/. If it is a real directory, symlinking failed — fix that before you spend an afternoon debugging edits that go nowhere.

Package-local vendor/. Running composer install inside packages/acme/utilities creates a nested vendor/. Delete it (or gitignore it) — the host app’s autoloader already resolves the package, and a nested vendor directory causes duplicate-class and wrong-version-autoloader bugs that are genuinely nasty to diagnose.

Config publishing tags. Namespace every publishes() call with a package-specific tag (utilities-config, utilities-migrations). Generic tags collide across packages and vendor:publish --tag=config will do unpredictable things.

Extension points. Workbench itself is intentionally thin — its only real extension surface is your host app’s composer.json and the package’s extra.laravel block. If you need custom scaffolding (your org’s licence header, CI config, Pint rules), the right move is a small internal Artisan generator that calls the Workbench commands or copies a stub directory — not patching the read-only component.

When not to use it. Workbench is heavyweight: you carry a full Laravel app around. For a tiny pure-PHP helper library, a bare composer init repo plus Testbench is faster and produces a cleaner artefact. Reach for Workbench when the package genuinely needs routes, migrations, Blade, queue jobs, or anything else that wants a booted application.

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.
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo
bernskioldmedia/laravel-harvest
bernskiold/laravel-data-scrubber
bernskiold/laravel-currency-converter
bernskiold/laravel-campaign-monitor
bernskioldmedia/laravel-autotranslate
siberfx/telegram-bot
roy-sandip/laravel-view-extended
gregsphotography/site-builder-core
roy-sandip/laravel-view
anodyne/blade-fluentui-icons
webx-ui/module-catalog-collections