I'm currently organising the third edition Full Stack Europe. It's a conference in Antwerp, Belgium in October for developers who want to learn across the stack. We have a great line up with lots of familiar faces! Register your ticket now!

Simplifying service providers in Laravel packages

Original – by Freek Van der Herten – 3 minute read

In almost every Laravel package, there is a ServiceProvider class responsible for registering bindings package resources such as config files, views, migrations, ...

While preparing a workshop that I gave on package development, I watched some videos of our own Laravel package training video course again. While watching the videos on using the service provider again, I had the idea to simplify how resources can be registered.

Meanwhile, I've fleshed out the idea and release it as a new package called laravel-package-tools. In this blog post, I'd like to introduce the package to you.

Introducing the PackageServiceProvider

Let's look at a service provider that registers a config file, migrations, views, translations, and a command. It uses several functions that are explained in Laravel's package development docs.

namespace YourVendor\YourAwesomePackage;

use Illuminate\Support\ServiceProvider;
use YourVendor\YourAwesomePackage\Commands\YourAwesomeCommand;

class YourAwesomePackageServiceProvider extends ServiceProvider
{
    public function boot()
    {
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__ . '/../config/laravel-awesome-package.php' => config_path('laravel-awesome-package.php'),
            ], 'config');

            $this->publishes([
                __DIR__ . '/../resources/views' => base_path('resources/views/vendor/laravel-awesome-package'),
            ], 'views');

            $migrationFileName = 'create_package_table.php';
            if (! $this->migrationFileExists($migrationFileName)) {
                $this->publishes([
                    __DIR__ . "/../database/migrations/{$migrationFileName}.stub" => database_path('migrations/' . date('Y_m_d_His', time()) . '_' . $migrationFileName),
                ], 'migrations');
            }

            $this->commands([
                YourAwesomeCommand::class,
            ]);
        }

        $this->loadViewsFrom(__DIR__ . '/../resources/views', 'laravel-awesome-package');
    }

    public function register()
    {
        $this->mergeConfigFrom(__DIR__ . '/../config/laravel-awesome-package.php', 'laravel-awesome-package');
    }

    public static function migrationFileExists(string $migrationFileName): bool
    {
        $len = strlen($migrationFileName);
        foreach (glob(database_path("migrations/*.php")) as $filename) {
            if ((substr($filename, -$len) === $migrationFileName)) {
                return true;
            }
        }

        return false;
    }
}

Our laravel-package-tools package contains a PackageServiceProvider class .

Let extend that PackageServiceProvider to vastly simplify the code above. The class below is equivalent to class in the previous code snippet.

namespace YourVendor\YourAwesomePackage;

use OriginalVendor\LaravelPackageTools\Package;
use OriginalVendor\LaravelPackageTools\PackageServiceProvider;
use YourVendor\YourAwesomePackage\Commands\YourAwesomeCommand;

class SkeletonServiceProvider extends PackageServiceProvider
{
    public function configurePackage(Package $package): void
    {
        $package
            ->name('your-awesome-package')
            ->hasConfigFile()
            ->hasViews()
            ->hasMigration('create_package_table')
            ->hasCommand(YourAwesomeCommand::class);
    }
}

That is way cleaner, right? I've already updated some of our packages to make use of our PackageServiceProvider. Here's the service provider from laravel-backup.

namespace Spatie\Backup;

// import statements omitted for brevity

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,
            ]);
    }

    public function packageRegistered()
    {
        $this->app['events']->subscribe(EventHandler::class);

        $this->app->singleton(ConsoleOutput::class);

        $this->app->bind(CleanupStrategy::class, config('backup.cleanup.strategy'));
    }
}

In closing

To know more about the PackageServiceProvider header over to the readme of laravel-package-tools on GitHub.

I've not PRed these changes to Laravel because the functions provided by PackageServiceProvider assume an opinionated package structure.

To kickstart your next Laravel package, you could consider using our Laravel package skeleton, which already uses the shiny new PackageServiceProvider out of the box. Here's a video that explains how to use the Laravel package skeleton (spoiler: it's very easy).

Be sure to take a look at this list of Laravel and PHP packages our team has released previously. I'm pretty sure there's something there for your next project.

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

What are your thoughts on "Simplifying service providers in Laravel packages"?

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

Webmentions

Roman Pronskiy liked on 7th March 2021
Pika liked on 7th March 2021
Syed Sirajul Islam Anik liked on 7th March 2021
Mostafa A. ツ replied on 6th March 2021
Niiceee I am trying to access "Laravel package skeleton" through github.com/spatie/laravel… as mentioned in the article but its a 404
Ahmed Nagi retweeted on 6th March 2021
Ahmed Nagi liked on 6th March 2021
Parthasarathi G K liked on 6th March 2021
Babul A. Mukherjee liked on 5th March 2021
Salman Zafar liked on 5th March 2021
Santosh Khanal liked on 5th March 2021
Montse ♥ liked on 5th March 2021
Tauseef shah liked on 5th March 2021
Daniel liked on 5th March 2021
ArielMejiaDev liked on 5th March 2021
Hassan Haseed liked on 5th March 2021
ArielMejiaDev retweeted on 5th March 2021
Loyce LEICHNIG liked on 5th March 2021
Ahmet Mirzabeyoğlu retweeted on 5th March 2021
💥 Rama Adi Nugraha retweeted on 5th March 2021
Ahmet Mirzabeyoğlu liked on 5th March 2021
José Cage liked on 5th March 2021
Lennart Fischer liked on 5th March 2021
Maarten Buis liked on 5th March 2021
Alfie Hopkin liked on 5th March 2021
Marius Engen Haugen liked on 5th March 2021
فرودو liked on 5th March 2021
WaveHack liked on 5th March 2021
Loris liked on 5th March 2021
Simon Blonér liked on 5th March 2021
Calefyb retweeted on 5th March 2021
Peter Brinck 🤘 liked on 5th March 2021
theonlyub liked on 5th March 2021
yurinascimento liked on 5th March 2021
Danilo Polani liked on 5th March 2021
Tom Witkowski liked on 5th March 2021
Mark Topper liked on 5th March 2021
Abdalrahman Almesery liked on 5th March 2021
Rafael Costa liked on 5th March 2021
Muhammed Sarı replied on 5th March 2021
Gonna try it this afternoon. Thanks a lot!
Thore Sünert liked on 2nd February 2021
Knight  liked on 31st January 2021
Gabo (Gabriel) liked on 29th January 2021
Roman Pronskiy liked on 29th January 2021
Peter Brinck 🤘 liked on 28th January 2021
Tauseef shah liked on 28th January 2021
tahapaksu liked on 28th January 2021
José Cage liked on 28th January 2021
Mo Khosh liked on 28th January 2021
E G I N liked on 28th January 2021
Ajay Makwana liked on 27th January 2021
Travis Elkins liked on 27th January 2021
ac3 liked on 27th January 2021
🤡 liked on 27th January 2021
TechShark liked on 26th January 2021
Calibrate Studio liked on 26th January 2021
Steven Whyte liked on 26th January 2021
Luca Degasperi liked on 26th January 2021
Priyanka liked on 26th January 2021
James Hemery liked on 26th January 2021
O ji nwayo e je liked on 26th January 2021
Lars Klopstra ⚡ liked on 26th January 2021
José Cage liked on 26th January 2021
Chris Woelk liked on 26th January 2021
Luke Downing liked on 26th January 2021
Julien RAVIA liked on 26th January 2021
Rocco Howard liked on 26th January 2021
Marvin Lepper liked on 26th January 2021
Godwin Effiòng liked on 26th January 2021
Gabi Rusu liked on 26th January 2021
Peter Brinck 🤘 liked on 26th January 2021
Monspeet liked on 26th January 2021
Nuno Maduro ✨ liked on 26th January 2021
Name is Blank liked on 26th January 2021
Erick Zarate 🦁 liked on 26th January 2021
ダビッド トレス liked on 26th January 2021
$fullName = 'Ishan Vyas'; liked on 26th January 2021
ダビッド トレス retweeted on 26th January 2021
/dev/faisal liked on 26th January 2021
Willan Correia liked on 26th January 2021
Devsome retweeted on 26th January 2021
Devsome liked on 26th January 2021
Luigi Cruz liked on 26th January 2021
Flavius 🍪 Constantin liked on 26th January 2021
Anatoli Nicolae liked on 26th January 2021
Mak Man liked on 26th January 2021
Jonny Nott liked on 26th January 2021
Fabrizio liked on 26th January 2021
Anders Bilfeldt liked on 25th January 2021
Omar Andrés Barbosa Ortiz liked on 25th January 2021
Nasirou Wagana liked on 25th January 2021
Nasirou Wagana retweeted on 25th January 2021
Salman Zafar liked on 25th January 2021
Goss 👨🏾‍💻 🇸🇳 liked on 25th January 2021
Tony B liked on 25th January 2021
TweetBeeg App liked on 25th January 2021
Tony retweeted on 25th January 2021
Nathan Langer liked on 25th January 2021
Oliver Stark liked on 25th January 2021
Nii Adjetey Sowah liked on 25th January 2021
James liked on 25th January 2021
Tony liked on 25th January 2021
ArielMejiaDev retweeted on 25th January 2021
Filip G liked on 25th January 2021
Filip G retweeted on 25th January 2021
ArielMejiaDev liked on 25th January 2021
Madalin Tache 🇷🇴 liked on 25th January 2021
Jānis Lācis liked on 25th January 2021
Argia liked on 25th January 2021
Owen Voke retweeted on 25th January 2021
Spatie retweeted on 25th January 2021
Julio Serpone retweeted on 25th January 2021
Maarten Buis liked on 25th January 2021
Rajeev Choudhary liked on 25th January 2021
a.m.a.hejazi@gmail.com liked on 25th January 2021
realCodeCraft liked on 25th January 2021
David Llop liked on 25th January 2021
Pakistan1st liked on 25th January 2021
Tauseef shah liked on 25th January 2021
JesusGarciaValadez liked on 25th January 2021
Owen Voke liked on 25th January 2021
Neeraj Tangariya liked on 25th January 2021
Matthew Poulter liked on 25th January 2021
Julio Serpone liked on 25th January 2021
Niels Kootstra liked on 25th January 2021
Paul Schuberth liked on 25th January 2021
Erick Patrick liked on 25th January 2021
Wyatt liked on 25th January 2021
theonlyub liked on 25th January 2021
Mathieu TUDISCO replied on 25th January 2021
OK. Didn't read this one 😇
Freek Van der Herten 📯 replied on 25th January 2021
I mentioned why not in the blogpost 👍
Mathieu TUDISCO replied on 25th January 2021
Don't you want to do a pull request to the core instead of releasing a package for that?
Pakistan1st replied on 25th January 2021
i knew you would do it after your initial idea tweet, great stuff thanks for that.
Matthieu liked on 25th January 2021
Balu retweeted on 25th January 2021
PHP Synopsis retweeted on 25th January 2021
Robin Dirksen liked on 25th January 2021
Tom Witkowski liked on 25th January 2021
Balu liked on 25th January 2021
Daniel Naxon liked on 25th January 2021
Tom Witkowski replied on 25th January 2021
This should/could get @laravelphp core logic! It would also define package structures and best practices. 🥳
Haz liked on 25th January 2021