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.
- A class or interface is bound to the Laravel service provider using a
'string'
. - A
Facade
returns the bound'string'
, to load the bound class or interface. - An
Alias
points to theFacade
- Laravel calls the
Alias
to access the class or interface
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.
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();
There's no particular order, which is another reason Facades can be confusing.
- Create an
Alias
to theFacade
class - Create the
Facade
classgetFacadeAccessor()
returns astring
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 theFacade
class - This binding returns an instance of the target class.
- The same known
- Register the
ServiceProvider
class
Final Step:
composer dump-autoload
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
.
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 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() { // }
}
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.
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.
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';
}
}
composer dump-autoload
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
Make a call to Alias::function()
to use the Facade.
user@dev:/var/www/html/project$ php artisan tinker
>>> Cats::sound()
=> "meow"
>>> exit
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.
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 CanineServiceProvider.php
app/Providers/CanineServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class CanineServiceProvider extends ServiceProvider
{
public function boot() { // }
public function register() { // }
}
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;
});
}
}
Inside a Laravel application, autoloading the service provider and registering the alias are handled by editing config\app.php
.
/*
|--------------------------------------------------------------------------
| 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,
],
/*
|--------------------------------------------------------------------------
| 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,
],
Laravel will Autoload classes within the application, you do not have to run composer dump-autoload
.
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
Make a call to Alias::function()
to use the Facade.
user@dev:/var/www/html/project$ php artisan tinker
>>> Dogs::sound()
=> "bark"
>>> exit