0

Building your own facade

Advanced5 min read·lv-04-006
framework

Concept

Building a custom Facade gives your package or application code a clean static interface to a container-bound service. The process is: create a service class, bind it in a service provider, create a Facade class with getFacadeAccessor(), register the alias.

Steps to create a Facade:

  1. Create the service class (app/Services/Cart.php).
  2. Bind it in a service provider (AppServiceProvider::register()).
  3. Create a Facade class (app/Facades/Cart.php) extending Illuminate\Support\Facades\Facade.
  4. Implement getFacadeAccessor() returning the container key.
  5. Register the alias in config/app.php's aliases array (optional — for short names).

PHPDoc for IDE support: Without type hints, IDEs don't know what methods the Facade proxies. Add @method annotations to the Facade class, or install barryvdh/laravel-ide-helper and run php artisan ide-helper:generate.

Facade testing: Because the Facade resolves from the container, calling Cart::swap(new MockCart()) in tests replaces the underlying instance. Or use Cart::shouldReceive() via Mockery.

When to create a Facade: When you have a service that's used frequently across many places (controllers, views, blade components), and DI injection would add noise. Don't create Facades for rarely-used services.

Code Example

php
<?php
// 1. Service class
namespace App\Services;

class Cart
{
    private array $items = [];

    public function add(int $productId, int $quantity = 1): void
    {
        $this->items[$productId] = ($this->items[$productId] ?? 0) + $quantity;
    }

    public function remove(int $productId): void
    {
        unset($this->items[$productId]);
    }

    public function total(): int
    {
        return array_sum(array_map(fn($qty, $id) => Product::find($id)->price * $qty,
            $this->items, array_keys($this->items)
        ));
    }

    public function count(): int
    {
        return array_sum($this->items);
    }

    public function items(): array { return $this->items; }
    public function clear(): void { $this->items = []; }
}
php
<?php
// 2. Bind in AppServiceProvider
$this->app->singleton(\App\Services\Cart::class, fn() => new \App\Services\Cart());
php
<?php
// 3. Facade class
namespace App\Facades;

use Illuminate\Support\Facades\Facade;

/**
 * @method static void add(int $productId, int $quantity = 1)
 * @method static void remove(int $productId)
 * @method static int total()
 * @method static int count()
 * @method static array items()
 * @method static void clear()
 * @see \App\Services\Cart
 */
class Cart extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return \App\Services\Cart::class;
    }
}
php
// 4. Alias in config/app.php
'aliases' => \Illuminate\Support\Facades\Facade::defaultAliases()->merge([
    'Cart' => App\Facades\Cart::class,
])->toArray(),
php
// 5. Usage anywhere
use App\Facades\Cart; // or just Cart:: if alias registered

Cart::add(productId: 42, quantity: 2);
echo Cart::count(); // 2
echo Cart::total(); // sum of items