Facades 
Facades provide a convenient, static interface to classes that are bound in the AvelPress service container. They act as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.
How Facades Work 
AvelPress facades are classes that provide access to objects from the container using a simple, static interface. All AvelPress facades extend the base AvelPress\Facades\Facade class.
Facade Class Structure 
<?php
namespace AvelPress\Facades;
class Route extends Facade
{
    /**
     * Get the registered name of the component.
     */
    protected static function getFacadeAccessor(): string
    {
        return 'router';
    }
}When you call a static method on a facade, AvelPress resolves the bound instance from the service container and runs the requested method against that object.
Built-in Facades 
AvelPress comes with several built-in facades:
Route Facade 
The Route facade provides access to the router:
<?php
use AvelPress\Facades\Route;
// Define routes
Route::get('/api/users', [UserController::class, 'index']);
Route::post('/api/users', [UserController::class, 'store']);
// Route groups
Route::prefix('api/v1')->group(function () {
    Route::get('/products', [ProductController::class, 'index']);
    Route::post('/products', [ProductController::class, 'store']);
});
// Route with middleware
Route::get('/admin/dashboard', [AdminController::class, 'dashboard'])
     ->guards(['manage_options']);DB Facade 
The DB facade provides access to the database layer:
<?php
use AvelPress\Facades\DB;
// Raw queries
$users = DB::select('SELECT * FROM users WHERE active = ?', [1]);
// Query builder
$products = DB::table('products')
    ->where('status', 'active')
    ->orderBy('created_at', 'desc')
    ->get();
// Transactions
DB::transaction(function () {
    DB::table('orders')->insert([
        'user_id' => 1,
        'total' => 99.99,
    ]);
    
    DB::table('order_items')->insert([
        'order_id' => DB::getPdo()->lastInsertId(),
        'product_id' => 1,
        'quantity' => 2,
    ]);
});Config Facade 
The Config facade provides access to configuration values:
<?php
use AvelPress\Facades\Config;
// Get configuration value
$appName = Config::get('app.name', 'Default App Name');
// Set configuration value
Config::set('app.debug', true);
// Check if configuration exists
if (Config::has('database.connections.mysql')) {
    // Configuration exists
}
// Get all configuration
$allConfig = Config::all();Schema Facade 
The Schema facade provides access to database schema operations:
<?php
use AvelPress\Facades\Schema;
// Create table
Schema::create('products', function ($table) {
    $table->id();
    $table->string('name');
    $table->decimal('price', 8, 2);
    $table->timestamps();
});
// Modify table
Schema::table('products', function ($table) {
    $table->string('sku')->after('name');
    $table->index('sku');
});
// Drop table
Schema::drop('old_table');
// Check if table exists
if (Schema::hasTable('products')) {
    // Table exists
}Creating Custom Facades 
You can create your own facades for any service bound in the container.
Step 1: Create the Service Class 
<?php
namespace App\Services;
class PaymentService
{
    private $gateway;
    private $logger;
    public function __construct(PaymentGateway $gateway, Logger $logger)
    {
        $this->gateway = $gateway;
        $this->logger = $logger;
    }
    public function charge($amount, $token): array
    {
        $this->logger->info("Processing payment for amount: {$amount}");
        
        try {
            $result = $this->gateway->charge($amount, $token);
            $this->logger->info("Payment successful", $result);
            return $result;
        } catch (\Exception $e) {
            $this->logger->error("Payment failed: " . $e->getMessage());
            throw $e;
        }
    }
    public function refund($chargeId, $amount = null): array
    {
        $this->logger->info("Processing refund for charge: {$chargeId}");
        
        return $this->gateway->refund($chargeId, $amount);
    }
    public function getTransactionHistory($userId): array
    {
        return $this->gateway->getTransactions($userId);
    }
}Step 2: Bind the Service in a Service Provider 
<?php
namespace App\Providers;
use AvelPress\Support\ServiceProvider;
use App\Services\PaymentService;
use App\Services\StripeGateway;
class PaymentServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton('payment', function ($app) {
            return new PaymentService(
                new StripeGateway(config('payment.stripe_key')),
                $app->make('logger')
            );
        });
    }
}Step 3: Create the Facade 
<?php
namespace App\Facades;
use AvelPress\Facades\Facade;
class Payment extends Facade
{
    /**
     * Get the registered name of the component.
     */
    protected static function getFacadeAccessor(): string
    {
        return 'payment';
    }
}Step 4: Use the Facade 
<?php
use App\Facades\Payment;
// Process a payment
$result = Payment::charge(99.99, $creditCardToken);
// Process a refund
$refund = Payment::refund($chargeId, 50.00);
// Get transaction history
$transactions = Payment::getTransactionHistory($userId);WordPress-Specific Facades 
WP Facade for WordPress Functions 
<?php
namespace App\Facades;
use AvelPress\Facades\Facade;
class WP extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return 'wp.helper';
    }
}
// Service class
namespace App\Services;
class WordPressHelper
{
    public function getCurrentUser()
    {
        return wp_get_current_user();
    }
    public function isUserLoggedIn(): bool
    {
        return is_user_logged_in();
    }
    public function getCurrentUserCan($capability): bool
    {
        return current_user_can($capability);
    }
    public function getOption($option, $default = false)
    {
        return get_option($option, $default);
    }
    public function updateOption($option, $value): bool
    {
        return update_option($option, $value);
    }
    public function addAction($hook, $callback, $priority = 10, $args = 1): void
    {
        add_action($hook, $callback, $priority, $args);
    }
    public function doAction($hook, ...$args): void
    {
        do_action($hook, ...$args);
    }
    public function applyFilters($hook, $value, ...$args)
    {
        return apply_filters($hook, $value, ...$args);
    }
}
// Usage
use App\Facades\WP;
if (WP::isUserLoggedIn() && WP::getCurrentUserCan('manage_options')) {
    WP::updateOption('my_plugin_setting', $newValue);
}Post Facade for WordPress Posts 
<?php
namespace App\Facades;
use AvelPress\Facades\Facade;
class Post extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return 'post.manager';
    }
}
// Service class
namespace App\Services;
class PostManager
{
    public function create(array $data): int
    {
        $postData = wp_parse_args($data, [
            'post_status' => 'publish',
            'post_type' => 'post',
        ]);
        $postId = wp_insert_post($postData);
        
        if (is_wp_error($postId)) {
            throw new \Exception('Failed to create post: ' . $postId->get_error_message());
        }
        return $postId;
    }
    public function update(int $postId, array $data): bool
    {
        $data['ID'] = $postId;
        $result = wp_update_post($data);
        
        return !is_wp_error($result);
    }
    public function delete(int $postId, bool $forceDelete = false): bool
    {
        $result = wp_delete_post($postId, $forceDelete);
        return $result !== false;
    }
    public function getMeta(int $postId, string $key, bool $single = true)
    {
        return get_post_meta($postId, $key, $single);
    }
    public function setMeta(int $postId, string $key, $value): bool
    {
        return update_post_meta($postId, $key, $value) !== false;
    }
    public function find(int $postId): ?\WP_Post
    {
        $post = get_post($postId);
        return $post instanceof \WP_Post ? $post : null;
    }
}
// Usage
use App\Facades\Post;
// Create a new post
$postId = Post::create([
    'post_title' => 'My New Post',
    'post_content' => 'This is the post content.',
    'post_type' => 'product',
]);
// Set meta data
Post::setMeta($postId, 'price', 99.99);
// Get meta data
$price = Post::getMeta($postId, 'price');Real-World Facade Examples 
Cache Facade 
<?php
namespace App\Facades;
use AvelPress\Facades\Facade;
class Cache extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return 'cache';
    }
}
// Service class
namespace App\Services;
class CacheService
{
    public function get($key, $default = null)
    {
        $value = wp_cache_get($key, 'my_plugin');
        return $value !== false ? $value : $default;
    }
    public function set($key, $value, $expiration = 3600): bool
    {
        return wp_cache_set($key, $value, 'my_plugin', $expiration);
    }
    public function forget($key): bool
    {
        return wp_cache_delete($key, 'my_plugin');
    }
    public function flush(): bool
    {
        return wp_cache_flush();
    }
    public function remember($key, $expiration, \Closure $callback)
    {
        $value = $this->get($key);
        
        if ($value !== null) {
            return $value;
        }
        $value = $callback();
        $this->set($key, $value, $expiration);
        return $value;
    }
}
// Usage
use App\Facades\Cache;
// Cache expensive operation
$users = Cache::remember('active_users', 3600, function () {
    return User::where('status', 'active')->get();
});
// Set cache
Cache::set('user_count', 1500, 1800);
// Get from cache
$count = Cache::get('user_count', 0);Mail Facade 
<?php
namespace App\Facades;
use AvelPress\Facades\Facade;
class Mail extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return 'mail';
    }
}
// Service class
namespace App\Services;
class MailService
{
    public function send($to, $subject, $message, $headers = []): bool
    {
        $defaultHeaders = [
            'Content-Type: text/html; charset=UTF-8',
        ];
        $headers = array_merge($defaultHeaders, $headers);
        return wp_mail($to, $subject, $message, $headers);
    }
    public function sendTemplate($to, $template, $data = [], $headers = []): bool
    {
        $subject = $this->renderTemplate($template . '_subject', $data);
        $message = $this->renderTemplate($template, $data);
        return $this->send($to, $subject, $message, $headers);
    }
    private function renderTemplate($template, $data = []): string
    {
        extract($data);
        
        ob_start();
        include plugin_dir_path(__FILE__) . "../templates/{$template}.php";
        return ob_get_clean();
    }
    public function queue($to, $subject, $message, $headers = []): void
    {
        // Add to queue for later processing
        wp_schedule_single_event(time() + 60, 'send_queued_email', [
            $to, $subject, $message, $headers
        ]);
    }
}
// Usage
use App\Facades\Mail;
// Send simple email
Mail::send('user@example.com', 'Welcome!', '<h1>Welcome to our site!</h1>');
// Send template email
Mail::sendTemplate('user@example.com', 'welcome', [
    'name' => 'John Doe',
    'login_url' => wp_login_url(),
]);
// Queue email for later
Mail::queue('user@example.com', 'Newsletter', $newsletterContent);Testing with Facades 
Facades make testing easier by allowing you to mock the underlying services:
<?php
use PHPUnit\Framework\TestCase;
use App\Facades\Payment;
use App\Services\PaymentService;
class PaymentTest extends TestCase
{
    public function testChargePayment()
    {
        // Create a mock of the payment service
        $mockPaymentService = $this->createMock(PaymentService::class);
        
        // Set up expectations
        $mockPaymentService->expects($this->once())
            ->method('charge')
            ->with(99.99, 'token_123')
            ->willReturn(['success' => true, 'charge_id' => 'ch_123']);
        // Replace the facade's underlying instance
        Payment::swap($mockPaymentService);
        // Test the facade
        $result = Payment::charge(99.99, 'token_123');
        $this->assertTrue($result['success']);
        $this->assertEquals('ch_123', $result['charge_id']);
    }
    protected function tearDown(): void
    {
        // Clear any swapped instances
        Payment::clearResolvedInstances();
    }
}Facade Best Practices 
1. Use Facades for Convenience, Not Everything 
<?php
// Good: Using facades for common operations
$users = DB::table('users')->where('active', 1)->get();
Route::get('/api/users', [UserController::class, 'index']);
// Consider dependency injection for complex services
class UserService
{
    public function __construct(
        private EmailService $emailService,
        private UserRepository $userRepository
    ) {}
}2. Provide Type Hints in IDEs 
Create a facade helper file for IDE support:
<?php
namespace App\Facades {
    /**
     * @method static bool charge(float $amount, string $token)
     * @method static array refund(string $chargeId, float $amount = null)
     * @method static array getTransactionHistory(int $userId)
     */
    class Payment extends \AvelPress\Facades\Facade {}
}3. Document Facade Methods 
<?php
namespace App\Facades;
use AvelPress\Facades\Facade;
/**
 * Cache Facade
 *
 * @method static mixed get(string $key, mixed $default = null)
 * @method static bool set(string $key, mixed $value, int $expiration = 3600)
 * @method static bool forget(string $key)
 * @method static bool flush()
 * @method static mixed remember(string $key, int $expiration, \Closure $callback)
 *
 * @see \App\Services\CacheService
 */
class Cache extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return 'cache';
    }
}Facades provide a clean, readable way to access services while maintaining the benefits of dependency injection and testability. They're particularly useful for common operations that you need to access throughout your application.
