Software design patterns are best defined in the words of Martin Fowler:
Patterns provide a mechanism for rendering design advice in a reference format. Software design is a massive topic, and when faced with a design problem, you must be able to focus on something as close to the problem as you can get.
The code that employs design patterns is easy to understand, maintain and extend. The purpose of this tutorial is to promote the facade in Laravel.
What Is the Facade Pattern?
According to the Gang of Four’s definition, the facade design pattern is a structural pattern that defines a simplified interface to a more complex subsystem. The pattern is based on creating a simple facade interface in front of the collection of required logic and methods. The facade itself maintains the dependencies.
The facade is very similar to the adapter and decorator patterns. The adapter acts like a bridge between two interfaces which are not compatible, while the decorator is more complex and used for dynamically changing the way objects behave.
What Are Laravel Facades?
Sweet syntax, which Laravel uses, makes writing code cleaner and easier to understand. Laravel facades are actually the syntactic sugar for service location.
Let’s take a look at Laravel Facade and the way it functions. The Cache
facade in Laravel looks like this:
Cache::get('key');
Although it might seem that it is using a load of static methods, Laravel actually provides an interface to classes that are available in the application’s service container. As you probably already know, the above-written code is equivalent to:
$app = app(); $app->make('cache')->get('key');
Laravel’s facade locates objects in vendor/Laravel/framework/src/Illuminate/Support/Facades
while the Cache facade is placed in Cache.php
:
namespace Illuminate\Support\Facades; class Cache extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'cache'; } }
When we use cache::get('key')
we are, in fact, calling the class above. Make sure to create the alias of the above-mentioned class in the config file config/app.php
:
'aliases' => [ //... 'Cache'=>Illuminate\Support\Facades\Cache::class,
The aliases are set up automatically by the Laravel auto-loader. Setting the name of the class to cache creates consistency with the facade. This option will most surely make people using facades more comfortable with your code.
The following three methods are crucial for generating a Facade:
-
__callStatic() PHP magic method, which is defined as the
getFacadeAccessor
method in the child class. - The Facade Root, which represents the underlying class the Facade calls methods on.
- The
resolveFacadeInstance
method is responsible for resolving the proper instance of the service.
The Implementation of the facade class methods:
//... public static function __callStatic($method, $args) { $instance = static::getFacadeRoot(); switch (count($args)) { case 0: return $instance->$method(); case 1: return $instance->$method($args[0]); case 2: return $instance->$method($args[0], $args[1]); case 3: return $instance->$method($args[0], $args[1], $args[2]); case 4: return $instance->$method($args[0], $args[1], $args[2], $args[3]); default: return call_user_func_array(array($instance, $method), $args); } }
__callStatic
basically calls the IoC Container to bind with the class. It also calls its (non-static) method by using the switch case via the PHP call_user_func_array()
function, passing the parameters array to the returning object getFacadeRoot()
method. The getFacadeRoot()
method is shown as follows:
public static function getFacadeRoot() { return static::resolveFacadeInstance( static::getFacadeAccessor() ); }
And the resolveFacadeInstance()
:
protected static function resolveFacadeInstance($name) { if (is_object($name)) returns $name; if (isset(static::$resolvedInstance[$name])) { return static::$resolvedInstance[$name]; } return static::$resolvedInstance[$name] = static::$app[$name]; }
As presented in the last line of the article, in the resolveFacadeInstance
method, Laravel returns the instance of the service locator. Because the locator is a pure instance of the original class, we conclude that the Laravel facade does not match with GoF’s facade pattern definition. Those are only service locations. Unlike the Laravel facade, the Real Facade makes writing unit tests difficult and sometimes even impossible, due to the creation of hard-coded dependencies.
For those who believe that DI via constructor is a better option than using Laravel facade, I would like to inform you that some extra configuration may be included.
How to Create the Laravel Facade
I want to create a check file Laravel Facade which is responsible for checking whether the input file is a pdf or not. In order to do this, first of all we need to create an Is Pdf
Class in App/MyFacade/IsPdf.php
:
namespace App\MyFacade; class IsPdf { private $pdf = "\x25\x50\x44\x46\x2D"; public function check($file) { return (file_get_contents($file, false, null, 0, strlen($this->pdf)) === $this->pdf) ? true : false; } }
Secondly, bind the class to the service provider. You will create the new service provider, which will be located in App\Providers\IsPdfServiceProvider:
namespace App\Providers; use Illuminate\Support\Facades\App; use Illuminate\Support\ServiceProvider; class IsPdfServiceProvider extends ServiceProvider { /** * Bootstrap the application services. * * @return void */ public function boot() { // } /** * Register the application services. * * @return void */ public function register() { App::bind('IsPdf', function() { return new \App\MyFacade\IsPdf; }); } }
Thirdly, create the Facade class, as an extension of the previously mentioned class Illuminate\Support\Facades\Facade
. You will create the class which will be located in App\Facades\IsPdfFacade.php
.
namespace App\Facades; use Illuminate\Support\Facades\Facade; class IsPdf extends Facade{ protected static function getFacadeAccessor() { return 'IsPdf'; } }
The last step is to register the Facade in config/app.php:
/* * Application Service Providers... */ App\Providers\IsPdfServiceProvider::class,
And the alias:
'IsPdf' => App\Facades\IsPdf::class
Congratulations! You have successfully created a Laravel facade. Feel free to test the Facade by using some codes, such as:
Route::get('/', function(){ IsPdf::check('/files/file.pdf'); });
Conclusion
Now we know that the Laravel facade makes it super easy to call methods, and injecting the actual dependencies could really pay off down the line. Of course Laravel Facade has its own advantages and disadvantages. It depends on the developer to select the right option.
For additional resources featuring Laravel, be sure to check out the offering in the marketplace.
Who knows, maybe this article will encourage you to develop framework-agnostic code and forget about using facades! Good luck!
Comments