Custom Laravel Packages

Creating a custom Laravel package.

Getting Started - autoload your namespace

If you don’t already have a Laravel project up and running, go ahead and create a fresh install of Laravel so you can build the package there.

The first real step in creating a Laravel package is to pick a namespace. Typically, this will be the camelcase name of a GitHub repository that you intend to use for distributing your package followed by a slash and the name of your package. For instance, our package is residing in the https://github.com/kirschbaum/laravel-spark-pages/ repository, so the camelcase equivalent is Kirschbaum/LaravelSparkPages.

Why bother with picking a namespace first? You’re going to need it for the following reasons:

  1. The namespace in your composer.json file enables your package to be loaded while you’re developing it. You must do this manually because you can’t “compose require” your package yet, which normally would autoload your package for you. In fact, go ahead and include your package manually by adding the line below to your psr–4 block in composer.json:
"psr-4": {
        "Kirschbaum\\LaravelSparkPages\\": "./packages/kirschbaum/laravel-spark-pages/src"
    }
  1. You need your namespace to create your directory structure. Go ahead and create a folder starting at the root level so that you will have the equivalent of “packages/kirschbaum/laravel-spark-pages/src” (adjusted for your namespace). The reason why you shouldn’t include your work within an existing package dependency folder (like “vendor”, for instance) is because if you ran composer update, your work might get deleted.
  2. You need to put your namespace at the top of most of the files within your package. For instance, the key file that loads your app is going to be a service provider that needs to have “[namespace]Kirschbaum\LaravelSparkPages;” at the top of it. If your package includes any controllers or models, they’re also going to have to have the same namespace at the top of each of them.
  3. When you create your package's composer.json file (more on this further below), you will be able to use this namespace to autoload your package into your users' Laravel projects.

Create a service provider file in your package

Now you can create the file you named above. It needs to be placed in your packages/kirschbaum/laravel-spark-pages/src folder (adjusted for your namespace), and you will also need to create a routes/web.php file for that folder as well. Go ahead and do that now. The service provider needs to be structured like so:

<?php
namespace Kirschbaum\LaravelSparkPages;

use Illuminate\Support\ServiceProvider;
class PackageServiceProvider extends ServiceProvider
{
    public function boot()
    {
    }

    public function register()
    {
    }
}

Note you should replace “PackageServiceProvider” with a relevant name for your app. Run “composer dump” so your changes to composer.json will take effect and this class will be recognized by Laravel.

Any routes your package may have should go in the boot() method, like so:

$this->loadRoutesFrom(__DIR__.'/../routes/web.php');

Any views your package will need to be registered in, fittingly, the register() method. You can do that like so:

$this->loadViewsFrom(__DIR__.'/../resources/views', 'laravel-spark-pages');

Notice for the last file, you are planning to load your package’s views from your main Laravel app in a folder named after your namespace that doesn’t exist yet (laravel-spark-pages). We’ll cover that in the below section “Copy migrations and customizable files to the Laravel app.”

Create a composer.json file in your package

It needs to be placed in your “packages/kirschbaum/laravel-spark-pages/” folder (adjusted for your namespace) and can follow this general structure:

{
    "name": "kirschbaum/laravel-spark-pages",
    "description": "Easy CMS-like page creation and editing for Laravel Spark",
    "type": "library",
    "license": "MIT",
    "authors": [
        {
            "name": "Bryan Miller",
            "email": "bryan@kirschbaumdevelopment.com"
        },
        {
            "name": "Nathan Kirschbaum",
            "email": "nathan@nathankirschbaum.com"
        }
    ],
    "keywords": ["laravel-spark", "content-management", "pages"],
    "require": {},
    "autoload": {
        "psr-4": {
            "Kirschbaum\\LaravelSparkPages\\": "src"
        }
    },
    "extra": {
        "laravel": {
            "providers": [
                "Kirschbaum\\LaravelSparkPages\\PackageServiceProvider"
            ]
        }
    }
}

Some of the above lines are optional, but the key line to include (adjusted for your namespace) is

"Kirschbaum\\LaravelSparkPages\\": "src"

Add the folders you’ll need

At this point, you can start to think of your package a little bit like a mini-Laravel app in terms of structure and how you’re going to add functionality. Put your Controllers, Models, and routes/web.php file into the “/src” folder alongside your service provider. Note that any routes you define in your routes/web.php folder will have to include the whole path of your package’s namespace when referencing controllers, such as this:

Route::get('/pages/{slug}/edit', '\Kirschbaum\LaravelSparkPages\PageController@edit');

Add a few more sibling folders to your “/src” folder as needed, such as “database”, and "resources" folders. The "resources" folder can be structured like it is in Laravel, with resources/view, resources/js, resources/css. In your database folder, you can add a migrations folder along with any migrations you might need. In your views folder, you can add blade files to your package’s controller and routes will need. If you have any JS files, put them in there, etc.

Copy migrations and customizable files to the Laravel app

You will have to publish any migrations you include in your package to the main Laravel app so they’ll get picked up when php artisan migrate gets run. You can do this by including the below block of code in your package’s service provider’s boot() method:

$this->publishes([
    __DIR__.'/../database/migrations/' => database_path('migrations')
], 'migrations');

and then by running the below command from the terminal (adjusted for your namespace):

php artisan vendor:publish --provider="Kirschbaum\LaravelSparkPages\PackageServiceProvider" --tag='migrations'

Note you will still have to run “php artisan migrate” for the migration to run.

If users may need to overwrite some of your package's default configurations, you can create a configuration file to get published in your users' config directory in a similar manner:

$this->publishes([
    __DIR__.'/../config/your-config-filename.php' => config_path('your-config-filename.php')
], 'config');

and again by running the command from the terminal (adjusted for your namespace):

php artisan vendor:publish --provider="Kirschbaum\LaravelSparkPages\PackageServiceProvider" --tag='config'

If there are any files in your package that users will likely want to customize, such as Blade, CSS or JS files, then include php artisan publish commands so that these files can be referenced outside of your package. They could be grouped together (as below) or separated out into individual calls to the publishes() method for more granular control. This way, if users ever want to update your package, all of their customized changes won’t get deleted as they can update their own copies of your package's files. You can do this by adding the below block to the same boot() method as mentioned above:

$this->publishes([
    __DIR__.'/../views/' => base_path('resources/views/vendor/laravel-spark-pages'),
    __DIR__.'/../js/' => base_path('resources/assets/js/vendor/laravel-spark-pages')
], 'assets');

and then by running the below command from the terminal (adjusted for your namespace):

php artisan vendor:publish --provider="Kirschbaum\LaravelSparkPages\PackageServiceProvider" --tag='assets'

And you’re ready to go!

Tags: Laravel, Composer

Interested in speaking with a developer?

Connect with us.
©2021 Kirschbaum Development Group LLC