Diving deep into Laravel source code reveals more patterns. In this case, we can spot a heavy usage of the Facade pattern.
One of the most interacted Facades - Route. We use it every day to define routes in our application:
Route::resource(...);Route::get(...);Route::post(...);Route::group(...);
And we don't even think about the fact that it is a Facade! Even if the use
statement at the top tells us that:
use Illuminate\Support\Facades\Route;
This was made into a Facade to allow us to use the Route
class without creating a new instance of it:
(new Route)->get(...);
Just imagine how much more code we would have to write if we had to create a new instance of the Route
class every time we wanted to define a new route.
So this is where Facades come in handy. They allow us to use the classes without creating new instances of them:
Illuminate/Support/Facades/Route.php
class Route extends Facade{ /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'router'; }}
And of course, in order to use the Route
Facade - there is some PHP magic happening behind the scenes:
Illuminate/Support/Facades/Facade.php
abstract class Facade{ // ... /** * Handle dynamic, static calls to the object. * * @param string $method * @param array $args * @return mixed * * @throws \RuntimeException */ public static function __callStatic($method, $args) { $instance = static::getFacadeRoot(); if (! $instance) { throw new RuntimeException('A facade root has not been set.'); } return $instance->$method(...$args); }}
This magic method allows us to call methods on any Facade as if we were calling them on the actual class.
Another common Facade is the DB
Facade. We use it to interact with the database:
DB::table('users')->get();DB::table('users')->where('name', 'John')->get();DB::transaction(...);DB::beginTransaction();
Once again, this feels like we are using a class, but in reality, we are using a Facade. In this case, the Facade behind it - has a unique method, prohibitDestructiveCommands
:
Illuminate/Support/Facades/DB.php
class DB extends Facade{ /** * Indicate if destructive Artisan commands should be prohibited. * * Prohibits: db:wipe, migrate:fresh, migrate:refresh, and migrate:reset * * @param bool $prohibit * @return void */ public static function prohibitDestructiveCommands(bool $prohibit = true) { FreshCommand::prohibit($prohibit); RefreshCommand::prohibit($prohibit); ResetCommand::prohibit($prohibit); WipeCommand::prohibit($prohibit); } /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'db'; }}
And as the method name suggests, it prohibits destructive commands. This is an excellent example of how we can extend the functionality of the Facade without changing the actual class.
Last but not least, the Gate
Facade. We use it to define authorization policies:
Gate::define(...);Gate::authorize(...);Gate::allows(...);Gate::denies(...);Gate::any(...);
These are all Facade methods, and using them is really common:
Controller
public function create(){ Gate::authorize('create', Post::class); return view('posts.create');}
Also, did you know you're using Facade in Blade templates? The @can
directive acts as the Gate
Facade:
@can(...)@cannot(...)
Or even, if you ever used ->can()
on your user Model:
$user->can(...);Auth::user()->can(...);
All of these are specific methods that we use, but in the end, they still call it the Gate
Facade.
But what are Facades? Why do they all extend the same class - Illuminate\Support\Facades\Facade
?
In short, this class provides additional functionality to the Facades. For example, in testing - we can mock them:
Cache::shouldReceive('get') ->once() ->with('key') ->andReturn('value');
This is possible because the Facade
class has a shouldReceive
method:
Illuminate/Support/Facades/Facade.php
// ... /** * Initiate a mock expectation on the Facade. * * @return \Mockery\Expectation */public static function shouldReceive(){ $name = static::getFacadeAccessor(); $mock = static::isMock() ? static::$resolvedInstance[$name] : static::createFreshMockInstance(); return $mock->shouldReceive(...func_get_args());} // ...
This is just one example of how the Facade
class extends the functionality of Facades and sets some default workflows for them.
If you are interested, we strongly recommend you to check the Facade
class in the Laravel source code