Step by step guide to Laravel package development

Step by step guide to Laravel package development

Are you curious how open-source Laravel packages are developed from scratch? Or are you unsure whether any specific functionality makes a good use case to be a Laravel package?

Let’s talk about these and other related questions today.

We all are fans of Laravel and its ecosystem for its simplicity and extensibility. Packages have been playing a major role in moving it forward. Especially official packages by Laravel, Spatie , Barry vd. , and others have made our lives and code better.

Why you should build a Laravel Package

Better coding skills

Open-source is magical. Putting your code out into the public makes you a bit vulnerable at the beginning but brings out the best of you. The fear of doing something wrong prevents every bit of you from doing the git push origin master but it’s always worth it. Laracasts’ series on open source project management can help a lot if you’re not well-versed with GitHub.

More (and better) work

You will build you brand leading to more work.

"Talk is cheap. Show me the code." - Linus Torvalds (creator of Linux and Git)

As developers and clients see your code and benefit from it, you will get more recognition.

Save time

You will save yourself considerable amount of time when you convert a piece of code, that you are using in more than one projects, into a package. “DRY” approach isn’t a new thing. We know its benefits.

In short, open-source package development makes your developer life better.

Now that we have a better idea about the need to go ahead with package development, let’s make one together now.

On a side note, contributing to an existing solution is also a great way to start if there is already an open-source package/library of your choice.

A new Laravel package from scratch

Note: I had said we will be building a real package. While thinking about an example, the only good option I could come up with was one of my own packages - Laravel Log Enhancer. It is short enough to cover quickly as well as plays with important parts of package development to learn the basics properly.

If you haven’t created any Laravel package before, I insist you to do this with me. It’ll be fun. Even if you make the same package, you’ll have more confidence and will be inspired to make changes or create a new one instantly. All the steps are undoable - you’ve nothing to lose. And true learning is all about doing the things practically. Our requirements are basic. If you’re already running any Laravel app locally, you’ve it all.

Let’s start.

1. New package directory

Create a directory with a name of the package. I will call it laravel-log-enhancer.

mkdir laravel-log-enhancer && cd laravel-log-enhancer

2. Love for git

Initialize a git repository. We’ll need GitHub repo to put the package online.

git init

3. composer.json

Every package starts with a composer.json file. Let’s create it. The easiest way to create it is to fire the init command.

composer init

Or you can just copy it from this file and make changes accordingly.

Package Composer json file

Commit the change. Not compulsory but we will do it after each unique step as a good habit.

4. Namespace and autoloading

We will put the main code of our package in src directory.

mkdir src && cd src

By the way, just like Laravel web app development, there are no hard rules about directory structure here but a helpful convention to follow.

We will update our composer.json file for 2 important things.

  • When a Laravel project uses our package, we can inform it about the location of the files of the package to be loaded by putting the path in PSR-4 autoloading part of composer.json.
  • To make the installation easy for the developer, we will put the location of the service provider, that should be automatically detected by Laravel, in extra section of the file.
...
"autoload": {
    "psr-4": {
        "Freshbitsweb\\LaravelLogEnhancer\\": "src/"
    }
},
"extra": {
    "laravel": {
        "providers": [
            "Freshbitsweb\\LaravelLogEnhancer\\LaravelLogEnhancerServiceProvider"
        ]
    }
}
...

By autoload, the parent Laravel web app knows about our base namespace i.e. Freshbitsweb\LaravelLogEnhancer and the files location which is the src directory. And Laravel’s awesome package discovery feature helps us remove a step from package installation process.

5. The service provider dilemma:

Many beginners stuck at this stage. Don’t worry. Let’s try to simplify this. A service provider is a file that can tell the parent Laravel application about various offerings of the package.

Common belief is that we always need a service provider for a package. That’s not the case. If your package doesn’t need to autoload any resources in advance, it can skip the service provider. But in most of the cases, for a package to be useful enough, service provider is required.

We will use it in our case to load the default configuration options of the package as well as offer an option to export those config options to the parent Laravel web app.

<?php

namespace Freshbitsweb\LaravelLogEnhancer;

use Illuminate\Support\ServiceProvider;

class LaravelLogEnhancerServiceProvider extends ServiceProvider
{
    /**
     * Publishes configuration file.
     *
     * @return  void
     */
    public function boot()
    {
        $this->publishes([
            __DIR__.'/../config/laravel_log_enhancer.php' => config_path('laravel_log_enhancer.php'),
        ], 'laravel-log-enhancer-config');
    }

    /**
     * Make config publishment optional by merging the config from the package.
     *
     * @return  void
     */
    public function register()
    {
        $this->mergeConfigFrom(
            __DIR__.'/../config/laravel_log_enhancer.php',
            'laravel_log_enhancer'
        );
    }
}

The code is quite simple here. In the boot method, we put the code to allow the developer to export the config options by simply writing:

php artisan vendor:publish -tag=laravel-log-enhancer-config

And in the register method, we tell the Laravel app to add the config options from our file into the web app config. Commit the update and let’s create our config file next.

6. Config options:

As a part of the convention, we will put our config file in config directory inside root folder next to src directory.

mkdir config && cd config && touch laravel_log_enhancer.php

Put the following code in the file.

<?php

return [
    'log_request_details' => true,

    'log_input_data' => true,

    'log_request_headers' => false,

    'log_session_data' => true,

    'log_memory_usage' => false,

    'log_git_data' => false,

    // You can specify the inputs from the user that should not be logged
    'ignore_input_fields' => ['password', 'confirm_password'],
];

This file contains few configuration options just like a regular Laravel config file. Commit.

7. Package classes

Final piece of code, for actual enhancement of Laravel logs, is the LogEnhancer.php file. To keep the length of the article digestible, I will directly link you to the actual class that you can copy. We also have RequestDataProcessor.php in the package. Put both of them in the src directory and you should be good to go.

8. Other important files

There are few other files that we need to add finally.

README file needs a special mention here. It is the face of the package. You should be writing detailed installation steps and other notes with proper flow in this file. Go through this GitHub guide for more.

Commit this change and now our package is ready to use.

What next? It’s time to release it.

How to release the package

If you haven’t pushed the package code to GitHub, do it first. If you don’t know how, this article can help.

Then, head over to Packagist , create an account or sign in, Click on Submit, specify the GitHub repo URL, … Steps are quite easy and self-explanatory. You should also enable Auto update of the package and consider Semantic versioning and releasing .

Packagist screenshot

Congrats. We’re done. Breath for a few minutes. There’s more.

Next, there are many other things are not part of the package development but are important for the development lifecycle and maintenance. Let’s go through them quickly.

How to use the package locally

We don’t have to push the updates online before testing it locally. Composer repositories path to the rescue.

Not just the package, we can do it with any repository. Here’s a great article explaining the details.

Let’s do it. Install a fresh Laravel app next to your package directory.

laravel new laravel-with-package

Make changes in it’s composer.json file

...
"repositories": [
    {
        "type": "path",
        "url": "../laravel-log-enhancer"
    }
],
...

Install the package by firing the following command.

composer require gauravmak/laravel-log-enhancer

Complete the installation steps and you can use the package now.

Testing

“I find that weeks of coding and testing can save me hours of planning.” - Unknown

They say - do not use a package without tests. They’re right. As the package gets bigger in size and more contributors enter the scene, some random commit can break the code functionality somewhere down the line. And we never want it to happen, right?

And it’ not hard. All we need to do it to check if the package is doing what it is supposed to do after every change/commit. In this package, we are adding processors to the Monolog handlers so that more data can be logged.

Unfortunately, as of now, Monolog doesn’t provide a method to fetch the processors of a handler so we are limited but let’s create a base of the testing for this package so that you know how to add tests to your custom package.

To start, we will add requirements to the composer.json file.

...
"require-dev": {
    "phpunit/phpunit": "^7.0",
    "orchestra/testbench": "~3.6.0"
 },
 "autoload-dev": {
    "psr-4": {
        "Freshbitsweb\\LaravelLogEnhancer\\Test\\": "tests"
    }
 },
 ...

We are basically testing a package which is outside a Laravel app. orchestral/testbench is a great package which can add the necessary dependencies so that we can test it as if we have all the Laravel goodies.

I have also added the following files to make the base of testing for our app.

Go through the README file of the testbench package for all details about how we can interact with Laravel while testing our package.

Integrations

There are so many helpful tools that integrate with GitHub repo and help us maintain the packages/libraries easily.

I suggest Travis CI for testing the package after each update and StyleCI for maintaing the code style.

I have integrated StyleCI in the package already. You should also consider adding badges to the README file.

Package Badges

Secret sauce of a successful package

You reap the benefits of making the package when more and more developers use it. And for that, there are so many factors starting from the basic idea behind the package to the stability and age of the package (stars).

If I were to decide on most important point for that, it would be the developer friendliness of the package. Yes, the exact reason why Laravel is popular.

Try to minimize the installation steps and the code that is required to use the package. Plus, it has to be readable enough that it doen’t need to annotated with long comments.

For example,

$users = User::with('roles')->get();

So simple, we know this operation involves join queries and other stuff. Still, you like to write it and read it. Do things on a similar level.

Final notes

That’s it for the day. We learned why package development is good for everyone and how to do it step-by-step using a real example. If you have any questions or get stuck anywhere, just add a comment below, I would be happy to help.

And If you think this can help other Laravel developers in your online network, I’d be happy to see you sharing this with them. Thanks.