Facades
- 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.
##Facades compared to dependencies InjectionOne 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:{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
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 functionsIn 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.
Facade | Class | 服务容器绑定 |
---|---|---|
App | Illuminate\Foundation\Application | app |
Artisan | Illuminate\Contracts\Console\Kernel | artisan |
Auth | Illuminate\Auth\AuthManager | auth |
Auth (Instance) | Illuminate\Contracts\Auth\Guard | auth.driver |
Blade | Illuminate\View\Compilers\BladeCompiler | blade.compiler |
Broadcast | Illuminate\Contracts\Broadcasting\Factory | |
Broadcast (Instance) | Illuminate\Contracts\Broadcasting\Broadcaster | |
Bus | Illuminate\Contracts\Bus\Dispatcher | |
Cache | Illuminate\Cache\CacheManager | cache |
Cache (Instance) | Illuminate\Cache\Repository | cache.store |
Config | Illuminate\Config\Repository | config |
Cookie | Illuminate\Cookie\CookieJar | cookie |
Crypt | Illuminate\Encryption\Encrypter | encrypter |
DB | Illuminate\Database\DatabaseManager | db |
DB (Instance) | Illuminate\Database\Connection | db.connection |
Event | Illuminate\Events\Dispatcher | events |
File | Illuminate\Filesystem\Filesystem | files |
Gate | Illuminate\Contracts\Auth\Access\Gate | |
Hash | Illuminate\Contracts\Hashing\Hasher | hash |
Lang | Illuminate\Translation\Translator | translator |
Log | Illuminate\Log\LogManager | log |
Illuminate\Mail\Mailer | mailer | |
Notification | Illuminate\Notifications\ChannelManager | |
Password | Illuminate\Auth\Passwords\PasswordBrokerManager | auth.password |
Password (Instance) | Illuminate\Auth\Passwords\PasswordBroker | auth.password.broker |
Queue | Illuminate\Queue\QueueManager | queue |
Queue (Instance) | Illuminate\Contracts\Queue\Queue | queue.connection |
Queue (Base Class) | Illuminate\Queue\Queue | |
Redirect | Illuminate\Routing\Redirector | redirect |
Redis | Illuminate\Redis\RedisManager | redis |
Redis (Instance) | Illuminate\Redis\Connections\Connection | redis.connection |
Request | Illuminate\Http\Request | request |
Response | Illuminate\Contracts\Routing\ResponseFactory | |
Response (Instance) | Illuminate\Http\Response | |
Route | Illuminate\Routing\Router | router |
Schema | Illuminate\Database\Schema\Builder | |
Session | Illuminate\Session\SessionManager | session |
Session (Instance) | Illuminate\Session\Store | session.store |
Storage | Illuminate\Filesystem\FilesystemManager | filesystem |
Storage (Instance) | Illuminate\Contracts\Filesystem\Filesystem | filesystem.disk |
URL | Illuminate\Routing\UrlGenerator | url |
Validator | Illuminate\Validation\Factory | validator |
Validator (Instance) | Illuminate\Validation\Validator | |
View | Illuminate\View\Factory | view |
View (Instance) | Illuminate\View\View |