Builder/manager pattern in Laravel

Published on Dec 10, 2022

In Hungary, there are many invoice services, and I want to give clients the choice of which one they prefer. To facilitate easy configuration and changes, I have implemented the Manager (builder) pattern. I will show you the pattern-related codes.

Let's create the config file, call it config/invoice.php.

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default driver
    |--------------------------------------------------------------------------
    |
    | You can change the invoice driver. Example: pdf, billingo, szamlazzhu
    */
    
    'driver' => env('INVOICE_DRIVER', 'pdf'),
];

Now create the Interface for the driver app/Driver/InvoiceInterface.php.

<?php

namespace App\Drivers;

interface InvoiceInterface
{
    public function run($data);
}

Create the Billingo invoice class app/Driver/Billingo.php.

<?php

namespace App\Drivers;

class Billingo implements InvoiceInterface
{
    public function run($data)
    {
        // Do your action
    }
}

Create the PDF invoice class app/Driver/PDF.php.

<?php

namespace App\Drivers;

class PDF implements InvoiceInterface
{
    public function run($data)
    {
        // Create a pdf
    }
}

Create the Manager class app/Managers/InvoiceManager.php where we load our Drivers.

<?php

namespace App\Managers;

use App\Drivers\Billingo;
use App\Drivers\PDF;
use Illuminate\Support\Manager;

class InvoiceManager extends Manager
{
    public function createBillingoDriver(): Billingo
    {
        return new Billingo();
    }

    public function createPdfDriver(): PDF
    {
        return new PDF();
    }

    public function getDefaultDriver()
    {
        return $this->config->get('invoice.driver');
    }
}

Register the InvoiceManager in the service provider app/Providers/AppServiceProvider.php.

    public function register(): void
    {
        $this->app->singleton('invoice', function ($app) {
            return new InvoiceManager($app);
        });
    }

Create a Facade to access the Invoice anywhere app/Facades/Invoice.php.

<?php

namespace App\Facades;

use App\Managers\InvoiceManager;
use Closure;
use Illuminate\Support\Facades\Facade;
use RuntimeException;

/**
 * @method static InvoiceManager getDefaultDriver()
 * @method static InvoiceManager driver(string $name)
 * @method static InvoiceManager extend(string $driver, Closure $callback)
 * @method static mixed run($data)
 */
class CreateInvoice extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     *
     * @throws RuntimeException
     */
    protected static function getFacadeAccessor()
    {
        return 'invoice';
    }
}

And in your controller or anywhere else, just call the following:

Invoice::run($data);

If you want to change the default driver, you can do so by editing the INVOICE_DRIVER variable in the .env file. Alternatively, you can change it on the fly by calling the Invoice with driver('billingo').

Invoice::driver('billingo')->run($data);

More example and explanation:
https://www.honeybadger.io/blog/laravel-manager-pattern/
https://laravel-news.com/manager-pattern-package-for-laravel
https://darkghosthunter.medium.com/laravel-the-hidden-manager-that-helps-you-with-any-driver-1a534d2b3570
https://refactoring.guru/design-patterns/builder/php/example