Oh Dear is the all-in-one monitoring tool for your entire website. We monitor uptime, SSL certificates, broken links, scheduled tasks and more. You'll get a notifications for us when something's wrong. All that paired with a developer friendly API and kick-ass documentation. O, and you'll also be able to create a public status page under a minute. Start monitoring using our free trial now.

Creating installer commands for Laravel packages

Original – by Freek Van der Herten – 5 minute read

One of the joys of using packages in the Laravel ecosystem is how easy they are to install. Packages can be pulled in using Composer, and Laravel will automatically discover them.

In this post, you'll learn how to easily add an install command, making it even easier for package users to start using a package.

Package install commands are great for the package user

Most packages will ask you to publish the config file, migrations, or some assets in their installation instructions. You probably have already executed commands such as:

php artisan vendor:publish --tag=awesome-package-config

php artisan vendor:publish --tag=awesome-package-migrations

php artisan vendor:publish --tag=awesome-package-assets

...

Some packages go about it a little bit better. Laravel Horizon, for instance, has a nifty install command that will:

  • publish the config file
  • create a Horizon service provider within your app
  • register that service provider in your config/app.php

screenshot

This is very convenient for the user to get up and running quickly.

In Horizon's source code, you can read the code that powers that install command. As a package developer, it's not too much trouble creating such a command. Still, specific tasks - such as registering a service provider in the config/app.php in the application - can take some time to get right.

Most package developers don't create install commands because it takes too long to get them right. That's why I spent some time making creating such install commands easy.

Easily create package install commands

Laravel Package Tools package is a package we made to help package creators. Here's what a typical service provider of a package could look like.

class BackupServiceProvider extends ServiceProvider
{
   public function boot()
     {
         $this->publishes([
             __DIR__.'/../config/backup.php' => config_path('backup.php'),
         ], 'config');

         $this->publishes([
             __DIR__.'/../resources/lang' => "{$this->app['path.lang']}/vendor/backup",
         ]);

         $this->loadTranslationsFrom(__DIR__.'/../resources/lang/', 'backup');
     }

     public function register()
     {
         $this->mergeConfigFrom(__DIR__.'/../config/backup.php', 'backup');
         
         $this->app->bind('command.backup:run', BackupCommand::class);
         $this->app->bind('command.backup:clean', CleanupCommand::class);
         $this->app->bind('command.backup:list', ListCommand::class);
         $this->app->bind('command.backup:monitor', MonitorCommand::class);
         
             $this->commands([
             'command.backup:run',
             'command.backup:clean',
             'command.backup:list',
             'command.backup:monitor',
         ]);
}

Using the PackageServiceProvider provided by spatie/laravel-package-tools, the code above can be rewritten like this:

class BackupServiceProvider extends PackageServiceProvider
 {
     public function configurePackage(Package $package): void
     {
         $package
             ->name('laravel-backup')
             ->hasConfigFile()
             ->hasTranslations()
             ->hasCommands([
                 BackupCommand::class,
                 CleanupCommand::class,
                 ListCommand::class,
                 MonitorCommand::class,
             ]);
     }
 }

In the latest version of laravel-package-tools, we've added new functionality to add an installer for your package. This is how you can do that.

class YourServiceProvider extends PackageServiceProvider
 {
     public function configurePackage(Package $package): void
     {
         $package
             ->name('your-package')
             ->hasConfigFile()
             ->hasTranslations()
             ->hasCommands([
                 BackupCommand::class,
                 CleanupCommand::class,
                 ListCommand::class,
                 MonitorCommand::class,
             ])
			 ->hasInstallCommand(function(InstallCommand $command) {
                $command
                    ->publishConfigFile()
                    ->publishMigrations()
                    ->askToRunMigrations()
                    ->copyAndRegisterServiceProviderInApp()
                    ->askToStarRepoOnGitHub();
            });
     }
 }

Using this code, your package will gain an install command that will:

  • copy the config file
  • publish the migration of your package
  • ask to run the migrations
  • copy a service provider stub to the app where the package is installed in. That service provider will also be registered in config/app.php of the app
  • ask if the repo should be starred on GitHub

Using hasInstallCommand is much quicker than writing an install command by hand.

We'll be using hasInstallCommand ourselves in our upcoming Dynamic Servers package. Here's what the code in the service provider of that package looks like.

// ...

 ->hasInstallCommand(function (InstallCommand $command) {
    $command
        ->publishConfigFile()
        ->copyAndRegisterServiceProviderInApp()
        ->publishMigrations()
        ->askToRunMigrations()
        ->askToStarRepoOnGitHub('spatie/laravel-dynamic-servers')
        ->endWith(function (InstallCommand $installCommand) {
            $installCommand->line('');
            $installCommand->info("We've added app\Providers\DynamicServersProvider to your project.");
            $installCommand->info("Feel free to customize it to your needs.");
            $installCommand->line('');
            $installCommand->info('You can view all docs at https://spatie.be/docs/laravel-dynamic-servers');
            $installCommand->line('');
            $installCommand->info('Thank you very much for installing this package!');
        });

Notice there's also an endWith function that you can use to make any customization you might like.

Here's what it looks like when we run dynamic-servers:install in a project where the package is installed.

screenshot

In closing

We hope this new functionality in spatie/laravel-package-tools will help package creators create an install command. I'm sure package users will be happy with such as command.

This isn't the first package that our team has built. On our company website, check out all our open source packages in this long list. If you want to support us, consider picking up any of our paid products.

Stay up to date with all things Laravel, PHP, and JavaScript.

You can follow me on these platforms:

On all these platforms, regularly share programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Comments

Maru Kari avatar

Creating custom install commands in Laravel helps you automate repetitive tasks, increase productivity, slope and ensure project consistency.

Comments powered by Laravel Comments
Want to join the conversation? Log in or create an account to post a comment.