spatie/laravel-package-tools
Laravel package helper from Spatie providing a base PackageServiceProvider to quickly register and publish config, migrations, routes, views, translations, assets, commands, install scripts, view components/composers, and shared view data with minimal boilerplate.
Installation:
composer require spatie/laravel-package-tools
Use the package-skeleton-laravel repo as a starting point for your package structure.
First Use Case:
Extend PackageServiceProvider in your main service provider file (src/YourPackageServiceProvider.php):
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package->name('your-package-name');
}
}
This sets up the foundation for all package features.
Package Configuration:
Define all package features in configurePackage() method. Example:
$package
->name('your-package')
->hasConfigFile()
->hasMigrations(['create_tables'])
->hasViewComponents('prefix', AlertComponent::class);
Feature Registration:
config/your-package.php and call hasConfigFile().database/migrations/ and register with hasMigration() or discoversMigrations().routes/ and register with hasRoute().src/Commands/ with hasCommand().Publishing: Users publish package assets via:
php artisan vendor:publish --tag=your-package-name-{feature}
Example tags: your-package-name-config, your-package-name-assets.
Install Command:
Create a custom installer command by extending Spatie\LaravelPackageTools\Commands\InstallCommand:
$package->hasInstallCommand(function(InstallCommand $command) {
$command
->publishConfigFile()
->publishMigrations()
->askToStarRepoOnGitHub();
});
$this->app->bind('your-package', function () {
return new YourPackageService();
});
configurePackage():
$package->registerEventListeners([
\YourPackage\Listeners\YourListener::class,
]);
src/Providers/ and register them:
$package->registerServiceProviders([
\YourPackage\Providers\YourProvider::class,
]);
Path Resolution:
configurePackage() are relative to the src/ directory. Incorrect paths (e.g., ../config/your-package.php) will cause failures.Migration Conflicts:
discoversMigrations()), ensure the database/migrations/ directory exists and contains valid migration files. Conflicts may arise if filenames or table names clash with existing migrations.Config Publishing:
hasConfigFile() is called but the config file doesn’t exist, the package will fail silently. Always verify the file exists at config/your-package.php.Route Caching:
hasRoute() are cached. Clear the route cache (php artisan route:clear) if routes aren’t loading after updates.View Component Namespace:
src/Components/. Misplaced components (e.g., in resources/views/) won’t register.\Log::info('Package configured:', ['name' => $package->name()]);
php artisan vendor:publish --tag=your-package-name --help
publishesServiceProvider(), ensure stub files (e.g., resources/stubs/YourProvider.php.stub) exist and are valid.Custom Publish Logic:
Override the getPublishableItems() method in your service provider to add custom publishable items:
public function getPublishableItems(): array
{
return [
'custom-file' => [
'from' => __DIR__.'/../path/to/file',
'to' => config_path('custom-file.php'),
],
];
}
Dynamic Configuration:
Use mergeConfigFrom in boot() to dynamically merge config:
public function boot()
{
$this->mergeConfigFrom(
__DIR__.'/../config/your-package.php',
'your-package'
);
}
Conditional Registration: Register features conditionally based on Laravel version or environment:
if (app()->version() >= '9.0') {
$package->hasViewComponents('prefix', Component::class);
}
Lifecycle Hooks:
Use registeringPackage(), bootingPackage(), and bootedPackage() to hook into package lifecycle:
protected function bootingPackage(): void
{
\Log::info('Package is booting!');
}
How can I help you explore Laravel packages today?