This package contains a PackageServiceProvider
that you can use in your packages to easily register config files,
migrations, and more.
Here's an example of how it can be used.
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
use MyPackage\ViewComponents\Alert;
use Spatie\LaravelPackageTools\Commands\InstallCommand;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasConfigFile()
->hasViews()
->hasViewComponent('spatie', Alert::class)
->hasViewComposer('*', MyViewComposer::class)
->sharesDataWithAllViews('downloads', 3)
->hasTranslations()
->hasAssets()
->publishesServiceProvider('MyProviderName')
->hasRoute('web')
->hasMigration('create_package_tables')
->hasCommand(YourCoolPackageCommand::class)
->hasInstallCommand(function(InstallCommand $command) {
$command
->publishConfigFile()
->publishMigrations()
->copyAndRegisterServiceProviderInApp()
->askToStarRepoOnGitHub()
});
}
}
Under the hood it will do the necessary work to register the necessary things and make all sorts of files publishable.
We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.
This package is opinionated on how you should structure your package. To get started easily, consider
using our package-skeleton repo to start your package. The
skeleton is structured perfectly to work perfectly with the PackageServiceProvider
in this package.
In your package you should let your service provider extend Spatie\LaravelPackageTools\PackageServiceProvider
.
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package) : void
{
$package->name('your-package-name');
}
}
Passing the package name to name
is mandatory.
To register a config file, you should create a php file with your package name in the config
directory of your
package. In this example it should be at <package root>/config/your-package-name.php
.
If your package name starts with laravel-
, we expect that your config file does not contain that prefix. So if your
package name is laravel-cool-package
, the config file should be named cool-package.php
.
To register that config file, call hasConfigFile()
on $package
in the configurePackage
method.
$package
->name('your-package-name')
->hasConfigFile();
The hasConfigFile
method will also make the config file publishable. Users of your package will be able to publish the
config file with this command.
php artisan vendor:publish --tag=your-package-name-config
Should your package have multiple config files, you can pass their names as an array to hasConfigFile
$package
->name('your-package-name')
->hasConfigFile(['my-config-file', 'another-config-file']);
Any views your package provides, should be placed in the <package root>/resources/views
directory.
You can register these views with the hasViews
command.
$package
->name('your-package-name')
->hasViews();
This will register your views with Laravel.
If you have a view <package root>/resources/views/myView.blade.php
, you can use it like
this: view('your-package-name::myView')
. Of course, you can also use subdirectories to organise your views. A view
located at <package root>/resources/views/subdirectory/myOtherView.blade.php
can be used
with view('your-package-name::subdirectory.myOtherView')
.
You can pass a custom view namespace to the hasViews
method.
$package
->name('your-package-name')
->hasViews('custom-view-namespace');
You can now use the views of the package like this:
view('custom-view-namespace::myView');
Calling hasViews
will also make views publishable. Users of your package will be able to publish the views with this
command:
php artisan vendor:publish --tag=your-package-name-views
Note:
If you use custom view namespace then you should change your publish command like this:
php artisan vendor:publish --tag=custom-view-namespace-views
You can share data with all views using the sharesDataWithAllViews
method. This will make the shared variable
available to all views.
$package
->name('your-package-name')
->sharesDataWithAllViews('companyName', 'Spatie');
Any Blade view components that your package provides should be placed in the <package root>/src/Components
directory.
You can register these views with the hasViewComponents
command.
$package
->name('your-package-name')
->hasViewComponents('spatie', Alert::class);
This will register your view components with Laravel. In the case of Alert::class
, it can be referenced in views
as <x-spatie-alert />
, where spatie
is the prefix you provided during registration.
Calling hasViewComponents
will also make view components publishable, and will be published
to app/Views/Components/vendor/<package name>
.
Users of your package will be able to publish the view components with this command:
php artisan vendor:publish --tag=your-package-name-components
You can register any view composers that your project uses with the hasViewComposers
method. You may also register a
callback that receives a $view
argument instead of a classname.
To register a view composer with all views, use an asterisk as the view name '*'
.
$package
->name('your-package-name')
->hasViewComposer('viewName', MyViewComposer::class)
->hasViewComposer('*', function($view) {
$view->with('sharedVariable', 123);
});
Any translations your package provides, should be placed in the <package root>/resources/lang/<language-code>
directory.
You can register these translations with the hasTranslations
command.
$package
->name('your-package-name')
->hasTranslations();
This will register the translations with Laravel.
Assuming you save this translation file at <package root>/resources/lang/en/translations.php
...
return [
'translatable' => 'translation',
];
... your package and users will be able to retrieve the translation with:
trans('your-package-name::translations.translatable'); // returns 'translation'
If your package name starts with laravel-
then you should leave that off in the example above.
Coding with translation strings as keys, you should create JSON files
in <package root>/resources/lang/<language-code>.json
.
For example, creating <package root>/resources/lang/it.json
file like so:
{
"Hello!": "Ciao!"
}
...the output of...
trans('Hello!');
...will be Ciao!
if the application uses the Italian language.
Calling hasTranslations
will also make translations publishable. Users of your package will be able to publish the
translations with this command:
php artisan vendor:publish --tag=your-package-name-translations
Any assets your package provides, should be placed in the <package root>/resources/dist/
directory.
You can make these assets publishable the hasAssets
method.
$package
->name('your-package-name')
->hasAssets();
Users of your package will be able to publish the assets with this command:
php artisan vendor:publish --tag=your-package-name-assets
This will copy over the assets to the public/vendor/<your-package-name>
directory in the app where your package is
installed in.
The PackageServiceProvider
assumes that any migrations are placed in this
directory: <package root>/database/migrations
. Inside that directory you can put any migrations.
To register your migration, you should pass its name without the extension to the hasMigration
table.
If your migration file is called create_my_package_tables.php.stub
you can register them like this:
$package
->name('your-package-name')
->hasMigration('create_my_package_tables');
Should your package contain multiple migration files, you can just call hasMigration
multiple times or
use hasMigrations
.
$package
->name('your-package-name')
->hasMigrations(['my_package_tables', 'some_other_migration']);
Calling hasMigration
will also make migrations publishable. Users of your package will be able to publish the
migrations with this command:
php artisan vendor:publish --tag=your-package-name-migrations
Like you might expect, published migration files will be prefixed with the current datetime.
You can also enable the migrations to be registered without needing the users of your package to publish them:
$package
->name('your-package-name')
->hasMigrations(['my_package_tables', 'some_other_migration'])
->runsMigrations();
Some packages need an example service provider to be copied into the app\Providers
directory of the Laravel app. Think
of for instance, the laravel/horizon
package that copies an HorizonServiceProvider
into your app with some sensible
defaults.
$package
->name('your-package-name')
->publishesServiceProvider($nameOfYourServiceProvider);
The file that will be copied to the app should be stored in your package
in /resources/stubs/{$nameOfYourServiceProvider}.php.stub
.
When your package is installed into an app, running this command...
php artisan vendor:publish --tag=your-package-name-provider
... will copy /resources/stubs/{$nameOfYourServiceProvider}.php
in your package
to app/Providers/{$nameOfYourServiceProvider}.php
in the app of the user.
You can register any command you package provides with the hasCommand
function.
$package
->name('your-package-name')
->hasCommand(YourCoolPackageCommand::class);
If your package provides multiple commands, you can either use hasCommand
multiple times, or pass an array
to hasCommands
$package
->name('your-package-name')
->hasCommands([
YourCoolPackageCommand::class,
YourOtherCoolPackageCommand::class,
]);
Instead of letting your users manually publishing config files, migrations, and other files manually, you could opt to add an install command that does all this work in one go. Packages like Laravel Horizon and Livewire provide such commands.
When using Laravel Package Tools, you don't have to write an InstallCommand
yourself. Instead, you can simply
call, hasInstallCommand
and configure it using a closure. Here's an example.
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\Commands\InstallCommand;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasConfigFile()
->hasMigration('create_package_tables')
->publishesServiceProvider('MyServiceProviderName')
->hasInstallCommand(function(InstallCommand $command) {
$command
->publishConfigFile()
->publishMigrations()
->askToRunMigrations()
->copyAndRegisterServiceProviderInApp()
->askToStarRepoOnGitHub('your-vendor/your-repo-name')
});
}
}
With this in place, the package user can call this command:
php artisan your-package-name:install
Using the code above, that command will:
- publish the config file
- publish the migrations
- copy the
/resources/stubs/MyProviderName.php.stub
from your package toapp/Providers/MyServiceProviderName.php
, and also register that provider inconfig/app.php
- ask if migrations should be run now
- prompt the user to open up
https://github.com/'your-vendor/your-repo-name'
in the browser in order to star it
You can also call startWith
and endWith
on the InstallCommand
. They will respectively be executed at the start and
end when running php artisan your-package-name:install
. You can use this to perform extra work or display extra
output.
use use Spatie\LaravelPackageTools\Commands\InstallCommand;
public function configurePackage(Package $package): void
{
$package
// ... configure package
->hasInstallCommand(function(InstallCommand $command) {
$command
->startWith(function(InstallCommand $command) {
$command->info('Hello, and welcome to my great new package!')
})
->publishConfigFile()
->publishMigrations()
->askToRunMigrations()
->copyAndRegisterServiceProviderInApp()
->askToStarRepoOnGitHub('your-vendor/your-repo-name')
->endWith(function(InstallCommand $command) {
$command->info('Have a great day!');
})
});
}
The PackageServiceProvider
assumes that any route files are placed in this directory: <package root>/routes
. Inside
that directory you can put any route files.
To register your route, you should pass its name without the extension to the hasRoute
method.
If your route file is called web.php
you can register them like this:
$package
->name('your-package-name')
->hasRoute('web');
Should your package contain multiple route files, you can just call hasRoute
multiple times or use hasRoutes
.
$package
->name('your-package-name')
->hasRoutes(['web', 'admin']);
You can put any custom logic your package needs while starting up in one of these methods:
registeringPackage
: will be called at the start of theregister
method ofPackageServiceProvider
packageRegistered
: will be called at the end of theregister
method ofPackageServiceProvider
bootingPackage
: will be called at the start of theboot
method ofPackageServiceProvider
packageBooted
: will be called at the end of theboot
method ofPackageServiceProvider
composer test
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.