sunaoka/laravel-facade-generator
Laravel Facade Generator adds an Artisan command that scaffolds facade classes for any class in your app, generating the accessor and docblocks automatically so you can call services statically without writing boilerplate by hand.
This is a scaffolding tool, not a runtime dependency. Treat it the way you treat make:model — install it, generate code, then forget it exists (or keep it in require-dev only).
composer require sunaoka/laravel-facade-generator --dev
Auto-discovery registers the package's service provider, so there is no manual wiring. Confirm the commands it actually exposes before reading docs — command names have shifted across versions:
php artisan list | grep -i facade
Typical first use case: you have a chunk of logic (a third-party SDK client, a pricing engine, an invoicing service) that you want to reach from controllers, Blade templates, and queued jobs without threading it through constructors everywhere.
php artisan make:facade Pricing
That should produce something on the order of app/Facades/Pricing.php plus a backing class (e.g. app/Services/PricingService.php or app/Pricing.php depending on config), and append a binding to AppServiceProvider or drop in a dedicated provider.
Look here first, in this order:
config/facade-generator.php after publishing (php artisan vendor:publish and pick the matching tag). It controls output namespaces and directory paths — get these right before generating anything, because renaming twenty generated classes later is tedious.readonly, interface-per-service).A good first exercise: generate one facade around a trivial service, call it from routes/web.php, and confirm Facade::getFacadeRoot() resolves. That five-minute loop surfaces 90% of the config problems.
Facade over an interface binding. The healthiest pattern with this package is to generate the facade and the class, then manually extract an interface and bind it in a provider:
// AppServiceProvider::register()
$this->app->singleton(PricingContract::class, PricingService::class);
// App\Facades\Pricing
protected static function getFacadeAccessor(): string
{
return PricingContract::class; // not a hard-coded class name
}
The generator will typically write the concrete class name into the accessor. Change it to the contract — it keeps the facade honest and makes swapping trivial in tests.
Edge-of-application usage. Facades are static proxies; they hide dependencies. Use them in Blade, Artisan commands, and HTTP controllers where injection is awkward. Keep them out of your domain/service internals — those should take constructor dependencies so they stay unit-testable without booting the container.
Swap in tests, don't mock the facade. Because the package just produces a standard Laravel facade, you inherit all the testing ergonomics:
Pricing::shouldReceive('quote')->once()->andReturn(1999);
// or, for fakes you control:
$this->swap(PricingContract::class, new FakePricing);
Prefer $this->swap() on the container binding when the backing class is stateful — shouldReceive on a facade works but gets brittle once you add methods.
Docblock contracts for IDE support. Generated facades rarely ship @method annotations. Add them, or run php artisan ide-helper:generate after ide-helper:models. Without this, Pricing::quote() shows up as an undefined-method warning in every editor, and your team will quietly stop using the facade.
Grouping. Resist generating one facade per class. Generate one facade per bounded concern (Billing, Search, FeatureFlags) with several methods on the backing service. Facade sprawl is the failure mode of this pattern.
Provider registration. If the generator emits a dedicated provider per facade, remember to register it in bootstrap/providers.php (Laravel 11+) or config/app.php. Auto-discovery does not cover providers the package itself created for you.
return 'pricing' needs a $this->app->bind('pricing', ...). Mismatches fail at call time with A facade root has not been set, not at boot — so the error surfaces late and looks unrelated.static::$cached can produce stale roots across tests in the same process. If a swapped binding isn't taking effect in a subsequent test, call Facade::clearResolvedInstances() in setUp.composer dump-autoload after generating. New files in new namespaces won't be picked up in long-running processes (octane, queue:work) until reloaded. Restart workers after generation.php artisan config:cache will freeze the package's config; if generated paths look wrong in a deployed environment, you're likely reading a stale cache. php artisan config:clear.make:facade produces something inconsistent with the last ten.use the package namespace. If you find yourself importing Sunaoka\... in app/, you've misread its role.Foo\Bar into directory paths sometimes produce app/facades/ on case-sensitive filesystems; verify namespace case against PSR-4 in composer.json.Domain/ layout), subclass or wrap the command rather than post-processing output — the generator is thin enough that a 30-line custom Artisan command is often the more maintainable answer.How can I help you explore Laravel packages today?