Published on

How to build a laravel package

Authors

We are going to look on how to make a laravel package. This should be an excting thing. You do not realise the power of packages until you have them. You could literally manage to move fast on some project and move slow on others depending on how you prioritize.


Introduction

I had a problem a few days ago where I wanted to add some logic into my application. The logic was something that I viewed more as a utility than a project specific feature. I could use the modular approach but that would tie the logic to the application. I wanted it to be reusable across a few laravel packages. So I had to come up with a laravel package that I could pull into the project. This way there is project independence.

What is a package

They are literally pieces of code that provide functionality to your application. e.g. Carbon which helps with dates.

Types of packages

Stand-alone/Framework agnostic - These are packages that do not depend on the framework they are being used in. They could be used in any PHP project and work just fine.

Framework packages - These are usually meant to enrich your framework application. They include framework specific tooling like routes, views, migrations etc.

For utility mostly, it's mostly advised to build framework agnostic libraries that can be used across all PHP applications.

My problem was I wanted to enrich all my laravel applications with these specific features and therefore focused on a laravel package.

We should be able to build this to show how it works.

Requirements

Getting started

We will start by running the following command:

bash
mkdir "{our-package}" && cd "{our-package}" && composer init

Composer will ask a few questions that you need to answer:

package name - this is what you name your package, it follows the <vendor>/<name> format. Recommended you use your <github-username>/<name-of-the-repo>.

Description - very self defining.

Author - could be you/your company

Minimum Stability - You could say dev (Most unstable) or if you need to work through the whole cycle you should go with stable

Package type - "Library"

PSR-4 autoloading - should follow the same pattern as the package name. PSR-4 is a standard for autoloading classes from file paths.

At the end you end up with a project like this:

bash
.
├── composer.json
└── src

What you need to know

Now with our dependencies defined, there are some things that a laravel package requires us to know.

Service providers

Laravel's container usually depends on the service provider. You can read more here. So we need to have a service provider for our package.

The service provider will usually bind all of our configurations to laravel's container. This allows seamless overriding of your defaults. For example a user may need to modify column names in your migrations. We can allow this by adding publishing in our package's service container. We could also publish other things like configs, views, routes etc.

When building the package, ensure that this is present in your application. We can add this by having it in our composer.json e.g.:

json
"extra": {
  "laravel": {
    "providers": [
      "Vendor\\Package\\PackageServiceProvider"
    ]
  }
}

Dependencies

Being a laravel package we will require some packages to help us in development. These dependencies are:

laravel/framework - which we will define in the require section of our composer.json

php - this is also a requirement for the PHP runtime. You need to specify the PHP version here.

If you need to add tests to your package (highly recommended) you can install:

We can focus on testing in a different article, for now we can just build it.

json
"require": {
  "php": "^8.2",
  // other packages that we need.
  // avoid having the framework as a dependency
}

Creating the Service Provider

Now we can create our ServiceProvider in the ./src folder.

bash
touch src/PackageServiceProvider.php

This is a class that extends Illuminate\Support\ServiceProvider and contains two methods, boot and register.

The boot method is where we add our stuff that can be published and the register method is where we register our config and app bindings that we may have in our package.

For example:

php
<?php

namespace Vendor\Package;

use Illuminate\Support\ServiceProvider;

class PackageServiceProvider extends ServiceProvider
{
  public function register()
  {
    $this->mergeConfigFrom(__DIR__ . '/../config/package.php', 'package');

    $this->app->bind(SomeInterface::class, Some::class);
  }

  public function boot()
  {
    if ($this->app->runningInConsole()) {
      $this->publishes([
        __DIR__ . '/../database/migrations' => database_path('migrations'),
      ], 'package-migrations');

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

    $this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
  }
}

You can add more things that you need to publish if you need to like views, routes etc. You can also bind stuff to the container as you would in the App service provider.

With all these setup then you can work on your package, install more dependencies that your package depends on, and go code mode. This is essentially how you will build your package provider in laravel.

Using Your Package

Once you've built your package, there are several ways to use it in your Laravel applications.

Installing from a Git Repository

If your package is hosted on GitHub, GitLab, or any other Git repository, you can install it directly by adding the repository to your project's composer.json:

json
"repositories": [
  {
    "type": "vcs",
    "url": "https://github.com/username/package-name"
  }
],
"require": {
  "vendor/package-name": "dev-main"
}

Then run:

bash
composer install

Installing from a Local Path

During development, you can install your package from a local directory:

json
"repositories": [
  {
    "type": "path",
    "url": "../path/to/your/package"
  }
],
"require": {
  "vendor/package-name": "@dev"
}

Then run:

bash
composer install

Using the Package in Your Laravel Application

Once installed, if you've configured the service provider in your package's composer.json extra section, Laravel will automatically discover and register it. You can then use your package's features immediately:

php
// Use published config
$value = config('package.some_key');

// Use package classes
use Vendor\Package\SomeClass;

$instance = new SomeClass();

If you need to publish the package's config or migrations:

bash
php artisan vendor:publish --tag=package-config
php artisan vendor:publish --tag=package-migrations