Skip to content

Instantly share code, notes, and snippets.

@poing
Last active September 6, 2024 14:32
Show Gist options
  • Save poing/c6d54614beabbbb1fd0f96d28a3288d2 to your computer and use it in GitHub Desktop.
Save poing/c6d54614beabbbb1fd0f96d28a3288d2 to your computer and use it in GitHub Desktop.
Laravel Facades

Understanding Facades in Laravel

What's a Facade?

The Laravel explination, shown below is confusing.

Facades provide a "static" interface to classes that are available in the application's service container. Laravel ships with many facades which provide access to almost all of Laravel's features. Laravel facades serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.

Many examples use Cache::get('key') to demonstrate how a Facade works. Comparing the following code to the utility that a Facade provides.

$val = Cache::get('key');
$val = app()->make('cache')->get('key');

But there's a flaw in this explanation.

make('cache') only works because it's already bound using a string known by the Laravel service container. Without the binding of 'cache', both lines in the code above will not work. And this binding, probably exists just to support the Cache Facade.

What a Facade really does...

  1. A class or interface is bound to the Laravel service provider using a 'string'.
  2. A Facade returns the bound 'string', to load the bound class or interface.
  3. An Alias points to the Facade
  4. Laravel calls the Alias to access the class or interface

How a Facade is really used...

Here are two 2 classes, in different locations, each with a simple function().

<?php
namespace Vendorname\Projectname\Some\Long\Path;
class Cats
{
    public function sound() { return 'meow'; }
}
<?php
namespace App\Http\Controllers;
class Dogs extends Controller
{
    public function sound() { return 'bark'; }
}

The normal way to access each Class::function() above, is like this:

$cat = Vendorname\Projectname\Some\Long\Path\Cats::sound();
$dog = App\Http\Controllers\Dogs::sound();

To use the above, without a Facade, you'll need to make the functions static.

With Facades, a developer could access each Class::function() like this:

$cat = Cats::sound();
$dog = Dogs::sound();

This is the "terse, expressive syntax" described in the Laravel documentation. Cats becomes a "static proxy" for Vendorname\Projectname\Some\Long\Path\Cats, the same applies to Dogs. Facades are an alias for the class, making it easier to write.

Why not just use an Alias?

You could use an Alias to make a class easier to call. But there are a few drawbacks to consider.

  • The class is not bound to the the Laravel service container
    • The class must be accessed for every call
  • It may affect unit testing
  • Each function() must be static, see below
Non-static method Vendorname\Projectname\Some\Long\Path\Cats::sound() should not be called statically

Facades also allow the developer to determine how a class is bound to the Laravel service provider. See the Laravel documentation on Binding.

$this->app->bind();
$this->app->singleton();
$this->app->when();

Creating a Facade in Laravel

There's no particular order, which is another reason Facades can be confusing.

  • Create an Alias to the Facade class
  • Create the Facade class
    • getFacadeAccessor() returns a string known by the Laravel service container.
  • Create the ServiceProvider class.
  • Register a binding to the Laravel service container
    • The same known string that's used in the Facade class
    • This binding returns an instance of the target class.
  • Register the ServiceProvider class

Final Step:

composer dump-autoload 

Creating a Facade for a Package

Since Facades are an important part of package development, I'll cover this method first. Creating a Facade for Vendorname\Projectname\Some\Long\Path\Cats as Cats.

Register the ServiceProvider Class

Here we see the PSR-4 Autoloader and Laravel package discovery in composer.json for the package. For this example, we'll call it FelineServiceProvider.

vendor/project/composer.json

"autoload": {
    "psr-4": {
      "Vendorname\\Projectname\\": "src/"
    }
},
"extra": {
    "laravel": {
        "providers": [
            "Vendorname\\Projectname\\FelineServiceProvider"
        ]
    }
}
Create the ServiceProvider Class

Create FelineServiceProvider.php I recommend placing this file under the NamespaceName of the PSR-4 package path. This helps avoid using __DIR__.'/../path/to' to access the directory that contains the current directory for other service provider tasks.

src/NamespaceName/FelineServiceProvider.php

<?php
namespace Vendorname\Projectname;
use Illuminate\Support\ServiceProvider;
class FelineServiceProvider extends ServiceProvider
{
    public function boot() { // }
    public function register() { // }
}
Register a Binding to the Laravel Service Container

Here we'll register() the binding 'string' to the Laravel service container as a singleton, so Vendorname\Projectname\Some\Long\Path\Cats is only called once. We'll call it kitten, used again later in the Facade class.

src/NamespaceName/FelineServiceProvider.php

<?php
namespace Vendorname\Projectname;
use Illuminate\Support\ServiceProvider;
class FelineServiceProvider extends ServiceProvider
{
    public function boot() { // }
    public function register() 
    {
        $this->app->singleton('kitten', function () {
            return new Some\Long\Path\Cats;
        });
    }
}

Notice that we omit the current namespace.

Create the Alias to the Facade Class

The Alias points to the Facade class. This alias will be the short name used to access the class.

There are at least three 3 ways an Alias can be added to the Laravel service container.

  • TheServiceProvider::boot()
  • config/app.php
  • composer.json

In this example, we'll use Illuminate\Foundation\AliasLoader in the FelineServiceProvider to add the Alias to the Laravel service contaner.

src/NamespaceName/FelineServiceProvider.php

<?php
namespace Vendorname\Projectname;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\ServiceProvider;
class FelineServiceProvider extends ServiceProvider
{
    public function boot() 
    { 
        $loader = AliasLoader::getInstance();
        $loader->alias('Cats', Facades\FelineFacade::class);
    }
    public function register() 
    {
        $this->app->singleton('kitten', function () {
            return new Some\Long\Path\Cats;
        });
    }
}

Notice that we omit the current namespace.

Create the Facade Class

The Facade simply returns the 'string' that points to the binding. getFacadeAccessor() will return kitten, pointing to the binding registered in the ServiceProvider class above.

src/NamespaceName/Facades/FelineFacade.php

<?php
namespace Vendorname\Projectname\Facades;
use Illuminate\Support\Facades\Facade;
class FelineFacade extends Facade
{
    protected static function getFacadeAccessor() 
    { 
        return 'kitten';
    }
}
PSR-4
composer dump-autoload 
Use the Binding

As previously mentioned, a Facade uses a string known by (bound to) the Laravel service container.

user@dev:/var/www/html/project$ php artisan tinker
>>> app()->make('kitten')->sound();
=> "meow"
>>> exit
Use the Facade

Make a call to Alias::function() to use the Facade.

user@dev:/var/www/html/project$ php artisan tinker
>>> Cats::sound()
=> "meow"
>>> exit

Creating a Facade for a Laravel Application

Many examples of a Facade, show how it's done within the Laravel framework. Once you undersand how to do it for a package, it's probably easier to do as part of an application. Here, we'll create a Facade for App\Http\Controllers\Dogs.

We'll change up the order too, since it doesn't matter.

Create the Facade Class

The Facade simply returns the 'string' that points to the binding. We'll have getFacadeAccessor() return puppy, used again later in the Facade ServiceProvider class.

app/Facades/CanineFacade.php

<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class CanineFacade extends Facade
{
    protected static function getFacadeAccessor() 
    { 
        return 'puppy';
    }
}
Create the ServiceProvider Class

Create CanineServiceProvider.php

app/Providers/CanineServiceProvider.php

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class CanineServiceProvider extends ServiceProvider
{
    public function boot() { // }
    public function register() { // }
}
Register a Binding to the Laravel Service Container

Here we'll register() the binding 'string' to the Laravel service container using bind, so App\Http\Controllers\Dogs is called every time. We're using puppy, same string as in the Facade class above.

app/Providers/CanineServiceProvider.php

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class CanineServiceProvider extends ServiceProvider
{
    public function boot() { // }
    public function register() 
    {
        $this->app->bind('puppy', function () {
            return new App\Http\Controllers\Dogs;
        });
    }
}
Service Providers and Aliases

Inside a Laravel application, autoloading the service provider and registering the alias are handled by editing config\app.php.

Register the ServiceProvider Class
    /*
    |--------------------------------------------------------------------------
    | Autoloaded Service Providers
    |--------------------------------------------------------------------------
    |
    | The service providers listed here will be automatically loaded on the
    | request to your application. Feel free to add your own services to
    | this array to grant expanded functionality to your applications.
    |
    */

    'providers' => [
        // ... existing entries
        App\Providers\CanineServiceProvider::class,
    ],
Create the Alias to the Facade Class
    /*
    |--------------------------------------------------------------------------
    | Class Aliases
    |--------------------------------------------------------------------------
    |
    | This array of class aliases will be registered when this application
    | is started. However, feel free to register as many as you wish as
    | the aliases are "lazy" loaded so they don't hinder performance.
    |
    */

    'aliases' => [
        // ... existing entries
        'Dogs' => App\Facades\CanineFacade::class,
    ],
PSR-4

Laravel will Autoload classes within the application, you do not have to run composer dump-autoload.

Use the Binding

As previously mentioned, a Facade uses a string known by (bound to) the Laravel service container. We'll call the puppy binding like this:

user@dev:/var/www/html/project$ php artisan tinker
>>> app()->make('puppy')->sound();
=> "bark"
>>> exit
Use the Facade

Make a call to Alias::function() to use the Facade.

user@dev:/var/www/html/project$ php artisan tinker
>>> Dogs::sound()
=> "bark"
>>> exit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment