Facades

Static interface for accessing Laravel services

graph LR A[Facade Call Cache::get] --> B[__callStatic Magic Method] B --> C[getFacadeAccessor] C --> D[Service Container] D --> E[Resolve Cache Instance] E --> F[Execute get Method] G[Facade Class] -->|Extends| H[Illuminate\Support\Facades\Facade]

Facades provide a static interface for accessing Laravel services. This pattern allows you to have static access to container services, while actually using Service Container.


Benefits of Facades:


  • Cleaner and more readable code with simpler syntax
  • Easy access to services without needing dependency injection
  • Better testability with ability to mock
  • Reduced boilerplate code
  • Familiar API for developers

How it Works:


Facades use magic method __callStatic to forward calls to the real instance in Service Container. When you call Cache::get('key'), Laravel automatically resolves the related instance from container.


Common Laravel Facades Examples:


  • <code>Cache::get()</code>, <code>Cache::put()</code> - for cache operations
  • <code>DB::table()</code>, <code>DB::select()</code> - for database operations
  • <code>Auth::user()</code>, <code>Auth::check()</code> - for authentication
  • <code>Route::get()</code>, <code>Route::post()</code> - for route definition
  • <code>Mail::send()</code> - for email sending
  • <code>Queue::push()</code> - for queue operations

Comparison with Dependency Injection:


Facades are easier but Dependency Injection is better for testability and maintainability. In Laravel 11.x, using Contracts (interfaces) with DI is preferred.

Examples

Using Facade

use Illuminate\Support\Facades\Cache;

Cache::put('key', 'value', 3600);
$value = Cache::get('key');

Instead of using Service Container directly, we use Facade.

Creating Custom Facade

<?php

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class PaymentGateway extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'PaymentGateway';
    }
}

A custom Facade that points to PaymentGateway in Container.

Using Facade in Controller

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class UserController extends Controller
{
    public function index()
    {
        $users = Cache::remember('users', 3600, function () {
            return DB::table('users')->get();
        });
        
        return view('users.index', compact('users'));
    }
}

Using Facades for easy access to cache and database.

Facade Mocking in Tests

<?php

namespace Tests\Feature;

use Illuminate\Support\Facades\Cache;
use Tests\TestCase;

class UserTest extends TestCase
{
    public function test_user_caching()
    {
        Cache::shouldReceive('get')
            ->once()
            ->with('user.1')
            ->andReturn(['id' => 1, 'name' => 'John']);
        
        $user = Cache::get('user.1');
        
        $this->assertEquals('John', $user['name']);
    }
}

You can mock Facades in tests.

Facade with Real-time Facades

<?php

// Instead of creating a Facade class, use real-time facade
use Facades\App\Services\PaymentGateway;

class OrderController extends Controller
{
    public function process()
    {
        // PaymentGateway is automatically resolved from container
        $result = PaymentGateway::charge(100);
        return $result;
    }
}

// In Service Provider:
app()->singleton('App\Services\PaymentGateway', function ($app) {
    return new StripePaymentGateway();
});

Real-time Facades allow you to use Facade without creating Facade class.

Facade Root Access

<?php

use Illuminate\Support\Facades\Cache;

// Get underlying instance
$cache = Cache::getFacadeRoot();

// Check if facade is resolved
if (Cache::resolved()) {
    // Facade has been resolved
}

// Get facade accessor name
$accessor = Cache::getFacadeAccessor();

You can access underlying instance and facade metadata.

Facade vs Dependency Injection

<?php

// Using Facade
use Illuminate\Support\Facades\Cache;

class UserService
{
    public function getUser($id)
    {
        return Cache::remember("user.{$id}", 3600, function () use ($id) {
            return User::find($id);
        });
    }
}

// Using Dependency Injection (Preferred)
use Illuminate\Contracts\Cache\Repository as Cache;

class UserService
{
    public function __construct(private Cache $cache) {}
    
    public function getUser($id)
    {
        return $this->cache->remember("user.{$id}", 3600, function () use ($id) {
            return User::find($id);
        });
    }
}

Dependency Injection is better for testability and maintainability, but Facades are easier.

Facade Aliases

<?php

// In config/app.php
'aliases' => [
    'Cache' => Illuminate\Support\Facades\Cache::class,
    'DB' => Illuminate\Support\Facades\DB::class,
    'Auth' => Illuminate\Support\Facades\Auth::class,
],

// Now you can use without full namespace
use Cache;
Cache::get('key');

// Or even without use statement (if alias is registered)
\Cache::get('key');

You can define aliases for Facades to make usage easier.

Use Cases

  • Quick and easy access to Laravel services in controllers and views
  • Usage in route closures for quick operations
  • Usage in blade templates for helper functions
  • Prototyping and rapid development
  • Legacy code migration requiring static access
  • Usage in console commands for quick access

Common Mistakes

  • Using Facades instead of Dependency Injection in service classes reducing testability
  • Using Facades in business logic causing tight coupling
  • Forgetting to import Facade causing errors
  • Using Facades in queue jobs which have different container instance
  • Not mocking Facades in tests causing integration tests instead of unit tests
  • Using Facades for everything which is overuse

Best Practices

  • Use Dependency Injection for service classes (preferred)
  • Use Facades for quick operations in controllers
  • Always mock Facades in tests
  • Use Contracts (interfaces) instead of Facades in dependency injection
  • Use real-time Facades for classes that don't have Facade class
  • Document Facades if creating custom Facade

Edge Cases

  • Facades in queue jobs which have different container instance
  • Facades in console commands which may have different context
  • Facades in event listeners which may be resolved at different times
  • Facades with circular dependencies
  • Facades in testing requiring proper mocking

Performance Notes

  • Facades overhead is very low and negligible
  • Facade resolution with caching in production improves performance
  • Using Facades has no negative impact on performance
  • Real-time Facades may be slightly slower but negligible

Security Notes

  • Ensure Facades use trusted sources
  • Use Facades for sensitive operations with caution
  • Ensure Facade accessor is properly bound

Interview Points

  • What is the difference between Facade and Dependency Injection?
  • How do Facades work?
  • When should you use Facades and when DI?
  • How can you mock Facade in tests?
  • What are the benefits and drawbacks of using Facades?
  • What are Real-time Facades and how do they work?

Version Notes

  • Laravel 11.x: Improved performance in Facade resolution
  • Laravel 11.x: Better support for real-time Facades
  • Laravel 10.x: Improved Facade mocking in tests
  • Laravel 9.x: Improved caching for Facades