Facades


Facades compared to dependency injection

    Facades compared to auxiliary functions
  • Facades working principle
  • Introduction
  • Facades provide a "static" interface for the application's service container. Laravel comes with many Facades that provide access to most features. Laravel Facades are actually "static proxies" for the underlying classes in the service container. Compared with traditional static methods, they can provide more flexible, easier to test, and more elegant syntax when used.
  • All Laravel Facades are defined in the Illuminate\Support\Facades
  • namespace. Therefore, we can easily use Facade:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {  
  return Cache::get('key');
  });

In the Laravel documentation, there are many sample codes that use Facades to demonstrate various functions of the framework.

When to use Facades

Facades have many advantages. They provide a simple, easy-to-remember syntax that eliminates the need to manually inject or configure long class names. Additionally, they make testing very easy due to their unique calls to PHP static methods.

However, there are some places that require special attention when using Facades. The main danger when using Facades is the expansion of class scope. Since Facades are very simple to use and do not require injection, we can inadvertently use many Facades in a single class, causing the class to become larger and larger. However, when using dependency injection, the more classes used, the longer the construction method will be. Visually, it is noticed that this class is a bit bulky. Therefore, when using Facades, pay special attention to controlling the size of the class to keep the scope of the class short.

{tip} When developing third-party extension packages that interact with Laravel, it is best to inject Laravel contracts instead of using Facades. Because extension packages are built outside of Laravel, you cannot use Laravel Facades to test helper functions

##Facades compared to dependencies Injection

One of the main benefits of dependency injection is the ability to swap the implementation of the injected class. Very useful when testing because you can inject a mock or stub and assert various methods on the stub.

Usually, it is impossible to mock or stub true static methods. However, Facades use dynamic methods to proxy the invocation of object methods parsed in the service container. We can also test Facades just like testing injected class instances. For example, like the following route:

use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {  
  return Cache::get('key');
 });

We can write the following test code with the parameters we expect to verify

Cache::get method:

use Illuminate\Support\Facades\Cache;
/**
 * 一个基础功能的测试。
 *
 * @return void
 */
 public function testBasicExample(){ 
    Cache::shouldReceive('get')   
          ->with('key')     
          ->andReturn('value');  
    $this->visit('/cache')   
        ->see('value');
    }

Facades Compared with auxiliary functions

In addition to Facades, Laravel also contains various "auxiliary functions" to implement these common functions, such as generating views, Trigger events, schedule tasks, or send HTTP responses. Many auxiliary functions have corresponding Facades. For example, the following Facades and helper functions have the same effect:

return View::make('profile');return view('profile');

There is no actual difference between Facades and helper functions. When you use a helper function, you can test it just like the corresponding Facade. For example, the following route:

Route::get('/cache', function () {    return cache('key');});

is implemented at the bottom layer, and the auxiliary function

cache actually calls the get method of the Cache Facade. Therefore, even though we are using a helper function, we can still verify the method by writing the following test code with the parameters we expect:

use Illuminate\Support\Facades\Cache;
/**
 * 一个基础功能的测试用例。
 *
 * @return void
 */
 public function testBasicExample(){ 
    Cache::shouldReceive('get')  
           ->with('key')    
           ->andReturn('value'); 
    $this->visit('/cache')  
        ->see('value');
     }

Facades working principle

In Laravel applications, Facade is a class that can access objects from the container. The core component is the Facade class. Whether it is Laravel's own Facades or custom Facades, they all inherit from the Illuminate\Support\Facades\Facade class.

Facade The base class uses the __callStatic() magic method, which will not be called until the object is parsed out of the container. In the example below, Laravel's caching system is called. By browsing this code, it can be assumed that the static method get is called in the Cache class:

<?php
   namespace App\Http\Controllers;
   use App\Http\Controllers\Controller;
   use Illuminate\Support\Facades\Cache;
   class UserController extends Controller{   
    /**
     * 显示给定用户的信息。
     *
     * @param  int  $id
     * @return Response
     */   
      public function showProfile($id) 
         {       
           $user = Cache::get('user:'.$id); 
           return view('profile', ['user' => $user]);  
          }
      }

Note that in the above code, we "import" Cache Facade. This Facade serves as a proxy for accessing the underlying implementation of the Illuminate\Contracts\Cache\Factory interface. Any calls we make using the Facade will be passed to the underlying instance of the Laravel caching service.

If we look at the Illuminate\Support\Facades\Cache class, you will find that there is no get static method in the class:

class Cache extends Facade{   
    /**
     * 获取组件的注册名称。
     *
     * @return string
     */  
       protected static function getFacadeAccessor() { 
            return 'cache'; 
        }
     }

Cache Facade inherits the Facade class and defines the getFacadeAccessor() method. The function of this method is to return the name of the service container binding. When a user calls any static method in the Cache Facade, Laravel resolves the cache binding from the service container and the object runs the requested method (in this case it is get method).

Real-time Facades

With real-time Facades, you can treat any class in your application as a Facade. To illustrate how this is used, let's look at another method. For example, let's say our Podcast model has a publish method. However, in order to publish a Podcast, we need to inject a Publisher instance:

<?php
   namespace App;
   use App\Contracts\Publisher;
   use Illuminate\Database\Eloquent\Model;
   class Podcast extends Model{  
     /**
     * 发布 Podcast。
     *
     * @param  Publisher  $publisher
     * @return void
     */ 
        public function publish(Publisher $publisher) 
           {     
              $this->update(['publishing' => now()]); 
              $publisher->publish($this);   
             }
        }

Injecting the publisher's implementation into the method, we can easily test this method since we can mock Injected publisher. However, it requires us to pass a publisher instance every time we call the publish method. Using live Facades we can maintain the same testability without having to explicitly pass a Publisher instance. To generate a real-time Facade, add Facades to the namespace of the imported class:

<?php
   namespace App;
   use Facades\App\Contracts\Publisher;
   use Illuminate\Database\Eloquent\Model;
   class Podcast extends Model{ 
    /**
     * 发布 Podcast。
     *
     * @return void
     */ 
        public function publish()  
          {   
               $this->update(['publishing' => now()]); 
                Publisher::publish($this);  
            }
       }

When using a real-time Facade, the publisher implementation will be prefixed by using the Facades The part of the interface or class name that appears later to solve the problem of the service container. When testing, we can use Laravel's built-in facade test helper function to simulate this method call:

<?php
  namespace Tests\Feature;use App\Podcast;
  use Tests\TestCase;use Facades\App\Contracts\Publisher;
  use Illuminate\Foundation\Testing\RefreshDatabase;
  class PodcastTest extends TestCase{  
    use RefreshDatabase;   
    /**
     * 一个测试演示。
     *
     * @return void
     */  
       public function test_podcast_can_be_published()  
         {    
             $podcast = factory(Podcast::class)->create(); 
             Publisher::shouldReceive('publish')->once()->with($podcast);  
             $podcast->publish(); 
            }
      }

Facade Class Reference

Below you can find each Facade class and its corresponding underlying class. This is a tool to find API documentation for a given Facade class. Key information for service container bindings is also included.

FacadeClass服务容器绑定
AppIlluminate\Foundation\Applicationapp
ArtisanIlluminate\Contracts\Console\Kernelartisan
AuthIlluminate\Auth\AuthManagerauth
Auth (Instance)Illuminate\Contracts\Auth\Guardauth.driver
BladeIlluminate\View\Compilers\BladeCompilerblade.compiler
BroadcastIlluminate\Contracts\Broadcasting\Factory 
Broadcast (Instance)Illuminate\Contracts\Broadcasting\Broadcaster 
BusIlluminate\Contracts\Bus\Dispatcher 
CacheIlluminate\Cache\CacheManagercache
Cache (Instance)Illuminate\Cache\Repositorycache.store
ConfigIlluminate\Config\Repositoryconfig
CookieIlluminate\Cookie\CookieJarcookie
CryptIlluminate\Encryption\Encrypterencrypter
DBIlluminate\Database\DatabaseManagerdb
DB (Instance)Illuminate\Database\Connectiondb.connection
EventIlluminate\Events\Dispatcherevents
FileIlluminate\Filesystem\Filesystemfiles
GateIlluminate\Contracts\Auth\Access\Gate 
HashIlluminate\Contracts\Hashing\Hasherhash
LangIlluminate\Translation\Translatortranslator
LogIlluminate\Log\LogManagerlog
MailIlluminate\Mail\Mailermailer
NotificationIlluminate\Notifications\ChannelManager 
PasswordIlluminate\Auth\Passwords\PasswordBrokerManagerauth.password
Password (Instance)Illuminate\Auth\Passwords\PasswordBrokerauth.password.broker
QueueIlluminate\Queue\QueueManagerqueue
Queue (Instance)Illuminate\Contracts\Queue\Queuequeue.connection
Queue (Base Class)Illuminate\Queue\Queue 
RedirectIlluminate\Routing\Redirectorredirect
RedisIlluminate\Redis\RedisManagerredis
Redis (Instance)Illuminate\Redis\Connections\Connectionredis.connection
RequestIlluminate\Http\Requestrequest
ResponseIlluminate\Contracts\Routing\ResponseFactory 
Response (Instance)Illuminate\Http\Response 
RouteIlluminate\Routing\Routerrouter
SchemaIlluminate\Database\Schema\Builder 
SessionIlluminate\Session\SessionManagersession
Session (Instance)Illuminate\Session\Storesession.store
StorageIlluminate\Filesystem\FilesystemManagerfilesystem
Storage (Instance)Illuminate\Contracts\Filesystem\Filesystemfilesystem.disk
URLIlluminate\Routing\UrlGeneratorurl
ValidatorIlluminate\Validation\Factoryvalidator
Validator (Instance)Illuminate\Validation\Validator 
ViewIlluminate\View\Factoryview
View (Instance) Illuminate\View\View
This article was first published on the LearnKu.com website.