0

Real-time facades — Facades\App\Services\PaymentService

Advanced5 min read·lv-04-003
laravel-src

Concept

Real-time Facades allow any class to be used as a Facade without creating a Facade class manually. By prepending Facades\ to a class's fully-qualified namespace, Laravel dynamically generates a Facade for it.

Syntax: use Facades\App\Services\PaymentService; — this creates an on-the-fly Facade for App\Services\PaymentService. You can then call PaymentService::charge(100) statically, which resolves App\Services\PaymentService from the container and calls charge(100) on the instance.

How it works: Laravel registers a PSR-4-compliant autoloader for the Facades\ namespace. When you import Facades\App\Services\PaymentService, Laravel's autoloader intercepts it, dynamically generates a Facade class with getFacadeAccessor() returning 'App\Services\PaymentService', and returns it.

Benefit: No need to create a app/Facades/PaymentService.php class. The Facades\ namespace handles it automatically.

Use cases: Quick prototyping, scripts, and cases where a full Facade class would be overkill. For production code, some teams prefer explicit Facade classes for clarity.

Testing real-time Facades: Works the same as regular Facades. Facades\App\Services\PaymentService::fake() or Facades\App\Services\PaymentService::spy().

IDE support: Real-time Facades don't have static type hints. The barryvdh/laravel-ide-helper package can generate phpdoc blocks, but it's less complete than explicit Facade classes with @see annotations.

Code Example

php
<?php
namespace App\Services;

class PaymentService
{
    public function charge(int $amountCents, string $paymentMethodId): array
    {
        // actual payment logic
        return ['status' => 'succeeded', 'id' => 'pi_xxx'];
    }

    public function refund(string $paymentIntentId): bool
    {
        return true;
    }
}
php
<?php
namespace App\Http\Controllers;

// Real-time Facade — no need to create App\Facades\PaymentService
use Facades\App\Services\PaymentService;

class OrderController extends Controller
{
    public function checkout(Request $request): JsonResponse
    {
        // Static call on real-time Facade
        // Resolves App\Services\PaymentService from container and calls charge()
        $result = PaymentService::charge(
            $request->validated('amount'),
            $request->validated('payment_method_id'),
        );

        return response()->json($result);
    }
}
php
<?php
// Testing real-time Facades
namespace Tests\Feature;

use Facades\App\Services\PaymentService;
use Illuminate\Foundation\Testing\TestCase;

class CheckoutTest extends TestCase
{
    public function test_checkout_charges_payment(): void
    {
        PaymentService::shouldReceive('charge')
            ->once()
            ->with(1000, 'pm_test_xxx')
            ->andReturn(['status' => 'succeeded', 'id' => 'pi_abc']);

        $this->postJson('/checkout', [
            'amount' => 1000,
            'payment_method_id' => 'pm_test_xxx',
        ])->assertOk();
    }
}