In the Laravel concept series, we will learn about the techniques that are considered the core of the Laravel framework: Service Containers, Service Providers, Facades, etc.
Concepts and benefits
If you have experience with Laravel, you have likely come across the following code:
Please pay attention to the “Cache::set(), Request::get(), DB::table(), Http::get()” which is the Facade.
Now, let’s discuss what exactly the facade is.
Facade provides a static-proxy class for the classes in the container, which makes it possible to call non-static functions statically.
Doesn’t that seem complicated? Let’s examine the following example to clarify.
Here is the standard method for calling a non-static function:
$cache = new CacheManager(app());
Here is the cache that is called statically:
With basic PHP knowledge, we all know the keyword :: helps to call a static function without instantiating the class object.
Benefits and use cases of Facades
- The syntax is short and easy to remember: looking back at the above example of Cache Facade, if used in a non-facade way, we will need to remember the name of the CacheManager class. In different situations, it can have completely different names like CacheService, CacheDriver blah blah… However, with the use of the name Cache, it is very close to the original concept (which is cache), so remembering is extremely easy. In addition, calling static is also very short (probably even 1 line should be shorter than 2 lines🤣)
- No need to care about dependencies or configs: look at the new CacheManager(app()), it is clear that when instantiating the class, we need to provide all the dependencies or config values for it. And if there is another dependency in the dependency, it will be a nightmare if it has to be fully declared. However, with Facade, we can completely ignore this problem because everything will be done by the framework.
- Hide actual implementation below: when we call Http::get(), we don’t need to know what it’s going to do, we simply need to call and enjoy the result😊. Or as an example of InvoiceFacade::send(‘email@example.com’, ‘2022-02’, ‘month’), reading through can easily understand the logic of sending invoices to firstname.lastname@example.org, data aggregated by month, and for February 2022. The send function can have a variety of logics such as getting data, filtering data, checking email, creating files, uploading to s3, blah blah… But when creating the facade, just calling the send function is enough. This is just a rough example, in practice, depending on the problem, we need to flexibly apply it.
- Easy to execute testing
How Facades work
Okay, now it’s time for us to figure out how Facade will work.
Let’s use the following example:
Above is an example using URL Facade. As you can see we used the class Illuminate\Support\Facades\URL and used it as usual. However, when we open that class up, boom, all we get is a class with a single function:
Wait, class Cache is extending class Façade. If we try opening that class, maybe the set() function is defined in it? After opening the class, oh, nothing at all 😒.
This is where Facade shows its magic.
Going back to PHP, when a static function is called that doesn’t exist in the class, a magic-method named __callStatic will be called instead. Let’s take a look at the code below:
Copy the above code and run the test. It can be easily seen that when we call the get() function, we will get the result “Get function”. This is simple to understand.
In the Test class, there is no set() function. Now the function __callStatic() will be called with the full function name and arguments. When referencing the result, we will have:
__callStatic: set function .
Open the Facade class again, and find the function __callStatic, we can understand that when we call the function URL::route(), __callStatic will be executed without throwing an Exception.
But where is the route() function of URL::route() actually, and how is it written?
Here are the steps Facade performs when calling __callStatic:
- Get the binding value of the instance through the getFacadeAccessor() function. In class Illuminate\Support\Facades\URL.php, we can see this function is returning the value of ‘url’
- Look in the service container for the value being bound to the facade accessor: here is the ‘url’ (refer to the Illuminate\Routing\RoutingServiceProvider.php class on line 64)
- Having resolved the binding value from the service container, we have an instance of the Illuminate\Routing\UrlGenerator.php class. This is where the code for the route() function is written. After resolved, we temporarily call the result $instanceResolved
- Call the function from $instanceResolved in a non-static way
Practice creating a Facade
Create class Invoice.php
Bind class Invoice to the Container
In the AppServiceProvider class, add the following code:
public function register(): void
Create class InvoiceFacade
Note that the return value of getFacadeAccessor() must match the value bound to the container, which is ‘invoice’.
Use Invoice Facade
public function Test()
Thus, we can see that we have called the send() function of the Invoice Facade without knowing how to configure it to initialize or implement it.
Going back to the Route::URL() example, what would happen if we remove the following line of code:
The answer is that the code still works properly. This is due to a mechanism called facade aliases.
Reopen the Illuminate\Support\Facades\Facade.php class and find the defaultAliases() function, which defines the aliases for the default facades, so we don’t need to use the class.
In the practice example, what if we also want to use an alias? Please follow the steps below:
Add the following code to configs/app.php:
‘aliases’ => Facade::defaultAliases()->merge([
Remove the following line where the facade is being used:
Please try again, the results are also the same
We have now gained an understanding of the Laravel Facade and its magical workings. In the upcoming post, we will delve into the Service Container technique. Thank you to all who have been following this post.
We would appreciate your feedback in the comments section below. 😍