0

Facade (Laravel) — the static proxy over a container-resolved instance

Beginner5 min read·eng-14-005
interviewlaravel-src

Definition

A Facade in Laravel is a static proxy that provides a static-style interface to an object resolved from the service container. When you call Cache::get('key'), PHP routes the static call to __callStatic() on the Cache facade class, which retrieves the real cache object from the container and forwards the call to it. The underlying object is not static — it is a fully injectable, testable, container-managed instance. The Facade is a syntactic convenience, not a pattern that bypasses the container.

In Practice

php
<?php

// Using the Facade — looks static, but isn't
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

Cache::put('user:1', $userData, ttl: 3600);
$user = Cache::get('user:1');

// What happens internally (simplified):
// Cache::__callStatic('get', ['user:1'])
//   → Cache::getFacadeRoot()         // retrieves from container
//   → app('cache')                   // the real CacheManager instance
//   → $cacheManager->get('user:1')   // actual method call

// Real-time facades — turn any class into a Facade on the fly
// Prefix the namespace with Facades\
use Facades\App\Services\PaymentService;
PaymentService::charge($amount); // no explicit binding needed

// Testing facades — swappable because the underlying object is container-managed
Cache::fake();       // replaces the real driver with an array store
Cache::shouldReceive('put')->once()->with('user:1', $userData, 3600);

In a Laravel codebase, Facades appear wherever a team prefers the concise static syntax over constructor injection. Log::info(), Route::get(), Event::dispatch(), Auth::user() — all Facades. The getFacadeAccessor() method on each Facade class returns the container key used to resolve the underlying instance.

In Context

In interviews, the key insight examiners want is: Facade calls are not actually static. They are syntactic sugar over container resolution. This means Facades are testable (via Facade::fake() and Facade::shouldReceive()), swappable (change the binding in the container, all Facades using that key change too), and not global state in the traditional sense — they are scoped to the container's lifecycle.

The design pattern the Laravel Facade implements is not the GoF Facade pattern (which simplifies a complex subsystem). It is closer to the Proxy pattern — intercepting calls and forwarding them to a real object. Laravel's use of the word "Facade" is technically a misnomer compared to the GoF definition, which is worth knowing if an interviewer asks you to distinguish them.

The trade-off versus constructor injection: Facades are convenient but obscure dependencies. A class using Cache::get() in its body does not declare CacheStore as a dependency in its constructor, making it harder to see what the class actually needs and harder to test by providing a different cache implementation. Many Laravel teams adopt a style guide that requires Facades only in service providers and configuration code, and constructor injection everywhere else.