Laravel Eloquent ORM: What is it and Why is it a Game-Changer?
Discover the power of Eloquent ORM for database interactions.
Facades provide a static interface to classes available in the service container. They serve as static proxies to underlying classes in the service container, providing expressive syntax while maintaining testability.
Think of Facades as:
When you call a static method on a facade, Laravel does this:
// This facade call...
Cache::get('key');
// Is equivalent to...
app('cache')->get('key');
// Which is equivalent to...
app()->make('cache')->get('key');
<?php
// Illuminate\Support\Facades\Facade (simplified)
namespace Illuminate\Support\Facades;
abstract class Facade
{
/**
* The resolved object instances.
*/
protected static $resolvedInstance;
/**
* Get the registered name of the component.
*/
protected static function getFacadeAccessor()
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
/**
* Handle dynamic, static calls to the object.
*/
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);
}
/**
* Get the root object behind the facade.
*/
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
/**
* Resolve the facade root instance from the container.
*/
protected static function resolveFacadeInstance($name)
{
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = app($name);
}
}
| Facade | Underlying Class | Service Container Binding |
|---|---|---|
| Cache | Illuminate\Cache\CacheManager | cache |
| DB | Illuminate\Database\DatabaseManager | db |
| Auth | Illuminate\Auth\AuthManager | auth |
| Illuminate\Mail\Mailer | mailer | |
| Route | Illuminate\Routing\Router | router |
| Request | Illuminate\Http\Request | request |
| Response | Illuminate\Contracts\Routing\ResponseFactory | response |
| Session | Illuminate\Session\SessionManager | session |
| Storage | Illuminate\Filesystem\FilesystemManager | filesystem |
| View | Illuminate\View\Factory | view |
<?php
// Using Cache facade
use Illuminate\Support\Facades\Cache;
class UserService
{
public function getUser($userId)
{
return Cache::remember("user.{$userId}", 3600, function () use ($userId) {
return User::find($userId);
});
}
public function clearUserCache($userId)
{
Cache::forget("user.{$userId}");
}
public function cacheUserStats($userId)
{
// Multiple cache operations
Cache::put("user.{$userId}.profile", $profile, 3600);
Cache::put("user.{$userId}.settings", $settings, 7200);
Cache::increment("user.{$userId}.views");
}
}
<?php
use Illuminate\Support\Facades\DB;
class ReportService
{
public function generateSalesReport($startDate, $endDate)
{
return DB::table('orders')
->select(
DB::raw('DATE(created_at) as date'),
DB::raw('COUNT(*) as total_orders'),
DB::raw('SUM(total) as total_revenue')
)
->whereBetween('created_at', [$startDate, $endDate])
->groupBy(DB::raw('DATE(created_at)'))
->get();
}
public function getTopProducts($limit = 10)
{
return DB::table('order_items')
->select(
'products.name',
DB::raw('SUM(order_items.quantity) as total_sold'),
DB::raw('SUM(order_items.quantity * order_items.price) as total_revenue')
)
->join('products', 'order_items.product_id', '=', 'products.id')
->groupBy('products.id', 'products.name')
->orderByDesc('total_sold')
->limit($limit)
->get();
}
public function transactionExample($orderData, $paymentData)
{
return DB::transaction(function () use ($orderData, $paymentData) {
// Create order
$orderId = DB::table('orders')->insertGetId($orderData);
// Process payment
DB::table('payments')->insert(array_merge($paymentData, [
'order_id' => $orderId
]));
// Update inventory
foreach ($orderData['items'] as $item) {
DB::table('products')
->where('id', $item['product_id'])
->decrement('stock_quantity', $item['quantity']);
}
return $orderId;
});
}
}
<?php
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeEmail;
use App\Mail\OrderConfirmation;
use App\Mail\PasswordReset;
class NotificationService
{
public function sendWelcomeEmail($user)
{
Mail::to($user->email)
->cc('support@example.com')
->bcc('admin@example.com')
->send(new WelcomeEmail($user));
}
public function sendBulkEmails($users, $subject, $template)
{
Mail::bcc($users)
->send(new BulkEmail($subject, $template));
}
public function sendWithAttachments($user, $filePath)
{
Mail::to($user->email)
->send(function ($message) use ($user, $filePath) {
$message->subject('Your Document')
->attach($filePath);
});
}
}
<?php
// app/Services/PaymentGateway.php
namespace App\Services;
class PaymentGateway
{
protected $apiKey;
protected $baseUrl;
public function __construct($apiKey, $baseUrl)
{
$this->apiKey = $apiKey;
$this->baseUrl = $baseUrl;
}
public function charge($amount, $token, $currency = 'USD')
{
// Implementation for charging a payment
return [
'id' => uniqid('ch_'),
'amount' => $amount,
'currency' => $currency,
'status' => 'succeeded'
];
}
public function refund($chargeId, $amount = null)
{
// Implementation for refund
return [
'id' => uniqid('re_'),
'charge_id' => $chargeId,
'amount' => $amount,
'status' => 'refunded'
];
}
public function createCustomer($email, $name = null)
{
// Create customer in payment system
return [
'id' => uniqid('cus_'),
'email' => $email,
'name' => $name
];
}
}
<?php
// app/Facades/Payment.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @method static array charge(float $amount, string $token, string $currency = 'USD')
* @method static array refund(string $chargeId, float $amount = null)
* @method static array createCustomer(string $email, string $name = null)
*
* @see \App\Services\PaymentGateway
*/
class Payment extends Facade
{
/**
* Get the registered name of the component.
*/
protected static function getFacadeAccessor(): string
{
return 'payment';
}
}
<?php
// app/Providers/AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\PaymentGateway;
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton('payment', function ($app) {
return new PaymentGateway(
config('services.payment.api_key'),
config('services.payment.base_url')
);
});
}
}
<?php
// app/Http/Controllers/PaymentController.php
namespace App\Http\Controllers;
use App\Facades\Payment;
class PaymentController extends Controller
{
public function processPayment()
{
$charge = Payment::charge(1000, 'tok_visa');
return response()->json([
'charge_id' => $charge['id'],
'status' => $charge['status']
]);
}
public function refundPayment($chargeId)
{
$refund = Payment::refund($chargeId);
return response()->json([
'refund_id' => $refund['id'],
'status' => $refund['status']
]);
}
}
<?php
// app/Services/AnalyticsService.php
namespace App\Services;
class AnalyticsService
{
protected $trackingId;
public function __construct($trackingId)
{
$this->trackingId = $trackingId;
}
public function trackPageView($page, $title = null)
{
// Track page view
logger("Page view: {$page}");
return $this;
}
public function trackEvent($category, $action, $label = null)
{
// Track custom event
logger("Event: {$category} - {$action} - {$label}");
return $this;
}
public function trackEcommerce($orderId, $total)
{
// Track ecommerce transaction
logger("Ecommerce: Order {$orderId} - {$total}");
return $this;
}
public function setUser($userId)
{
// Set user for tracking
logger("User set: {$userId}");
return $this;
}
}
// app/Facades/Analytics.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @method static \App\Services\AnalyticsService trackPageView(string $page, string $title = null)
* @method static \App\Services\AnalyticsService trackEvent(string $category, string $action, string $label = null)
* @method static \App\Services\AnalyticsService trackEcommerce(string $orderId, float $total)
* @method static \App\Services\AnalyticsService setUser(string $userId)
*
* @see \App\Services\AnalyticsService
*/
class Analytics extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'analytics';
}
}
// Usage with method chaining
Analytics::setUser('user_123')
->trackPageView('/checkout')
->trackEvent('purchase', 'completed')
->trackEcommerce('order_456', 99.99);
<?php
// app/Services/CloudStorage.php
namespace App\Services;
class CloudStorage
{
protected $driver;
protected $config;
public function __construct($driver, array $config = [])
{
$this->driver = $driver;
$this->config = $config;
}
public function put($path, $contents)
{
// Store file based on driver
logger("Storing file: {$path} using {$this->driver}");
return true;
}
public function get($path)
{
// Retrieve file
logger("Retrieving file: {$path} from {$this->driver}");
return "File contents";
}
public function exists($path)
{
// Check if file exists
return true;
}
public function driver($driver = null)
{
if ($driver) {
return new static($driver, config("filesystems.disks.{$driver}"));
}
return $this;
}
}
// app/Facades/CloudStorage.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @method static bool put(string $path, mixed $contents)
* @method static mixed get(string $path)
* @method static bool exists(string $path)
* @method static \App\Services\CloudStorage driver(string $driver = null)
*
* @see \App\Services\CloudStorage
*/
class CloudStorage extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'cloud-storage';
}
}
// app/Providers/AppServiceProvider.php
public function register(): void
{
$this->app->singleton('cloud-storage', function ($app) {
$defaultDriver = config('filesystems.default');
return new CloudStorage($defaultDriver, config("filesystems.disks.{$defaultDriver}"));
});
}
// Usage
CloudStorage::put('file.txt', 'Hello World');
CloudStorage::driver('s3')->put('backup/file.txt', 'Backup content');
<?php
// tests/Feature/PaymentControllerTest.php
namespace Tests\Feature;
use Tests\TestCase;
use App\Facades\Payment;
use Illuminate\Foundation\Testing\RefreshDatabase;
class PaymentControllerTest extends TestCase
{
use RefreshDatabase;
public function test_payment_processing()
{
// Mock the Payment facade
Payment::shouldReceive('charge')
->once()
->with(1000, 'tok_visa', 'USD')
->andReturn([
'id' => 'ch_123',
'amount' => 1000,
'status' => 'succeeded'
]);
$response = $this->postJson('/api/payments/process', [
'amount' => 1000,
'token' => 'tok_visa'
]);
$response->assertOk()
->assertJson([
'charge_id' => 'ch_123',
'status' => 'succeeded'
]);
}
public function test_payment_refund()
{
Payment::shouldReceive('refund')
->once()
->with('ch_123', 500)
->andReturn([
'id' => 're_456',
'charge_id' => 'ch_123',
'amount' => 500,
'status' => 'refunded'
]);
$response = $this->postJson('/api/payments/ch_123/refund', [
'amount' => 500
]);
$response->assertOk()
->assertJson([
'refund_id' => 're_456',
'status' => 'refunded'
]);
}
public function test_failed_payment()
{
Payment::shouldReceive('charge')
->once()
->andThrow(new \Exception('Payment failed'));
$response = $this->postJson('/api/payments/process', [
'amount' => 1000,
'token' => 'invalid_token'
]);
$response->assertStatus(422)
->assertJson([
'error' => 'Payment failed'
]);
}
}
// tests/Unit/Facades/AnalyticsFacadeTest.php
class AnalyticsFacadeTest extends TestCase
{
public function test_analytics_facade_method_chaining()
{
Analytics::shouldReceive('setUser')
->once()
->with('user_123')
->andReturnSelf();
Analytics::shouldReceive('trackPageView')
->once()
->with('/home', 'Home Page')
->andReturnSelf();
Analytics::shouldReceive('trackEvent')
->once()
->with('engagement', 'click', 'button')
->andReturnSelf();
// Test method chaining
$result = Analytics::setUser('user_123')
->trackPageView('/home', 'Home Page')
->trackEvent('engagement', 'click', 'button');
$this->assertInstanceOf(\App\Services\AnalyticsService::class, $result);
}
}
<?php
// tests/Unit/Facades/PaymentFacadeTest.php
class PaymentFacadeTest extends TestCase
{
public function test_facade_resolves_correct_instance()
{
$paymentInstance = Payment::getFacadeRoot();
$this->assertInstanceOf(\App\Services\PaymentGateway::class, $paymentInstance);
}
public function test_facade_accessor_returns_correct_binding()
{
$accessor = Payment::getFacadeAccessor();
$this->assertEquals('payment', $accessor);
}
public function test_facade_uses_singleton_binding()
{
$instance1 = Payment::getFacadeRoot();
$instance2 = Payment::getFacadeRoot();
$this->assertSame($instance1, $instance2);
}
}
<?php
// app/Facades/Cart.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @method static void addItem(int $productId, int $quantity = 1)
* @method static void removeItem(int $productId)
* @method static void updateQuantity(int $productId, int $quantity)
* @method static array getItems()
* @method static float getTotal()
* @method static int getItemCount()
* @method static void clear()
* @method static bool isEmpty()
*
* @see \App\Services\CartService
*/
class Cart extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'cart';
}
}
// app/Facades/Wishlist.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @method static void add(int $productId)
* @method static void remove(int $productId)
* @method static array getItems()
* @method static bool contains(int $productId)
* @method static void clear()
*
* @see \App\Services\WishlistService
*/
class Wishlist extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'wishlist';
}
}
// app/Http/Controllers/CartController.php
namespace App\Http\Controllers;
use App\Facades\Cart;
use App\Facades\Wishlist;
class CartController extends Controller
{
public function addToCart($productId)
{
Cart::addItem($productId);
return response()->json([
'success' => true,
'cart_count' => Cart::getItemCount(),
'message' => 'Product added to cart'
]);
}
public function moveToWishlist($productId)
{
Cart::removeItem($productId);
Wishlist::add($productId);
return response()->json([
'success' => true,
'message' => 'Product moved to wishlist'
]);
}
public function getCartSummary()
{
return response()->json([
'items' => Cart::getItems(),
'total' => Cart::getTotal(),
'item_count' => Cart::getItemCount(),
'wishlist_items' => Wishlist::getItems()
]);
}
}
<?php
// app/Facades/Notification.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @method static void sendWelcomeEmail(\App\Models\User $user)
* @method static void sendPasswordReset(\App\Models\User $user, string $token)
* @method static void sendOrderConfirmation(\App\Models\Order $order)
* @method static void sendShippingUpdate(\App\Models\Order $order, string $status)
* @method static void sendCustomNotification(string $type, array $data)
*
* @see \App\Services\NotificationService
*/
class Notification extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'notification';
}
}
// app/Http/Controllers/NotificationController.php
namespace App\Http\Controllers;
use App\Facades\Notification;
class NotificationController extends Controller
{
public function sendWelcome($userId)
{
$user = \App\Models\User::find($userId);
Notification::sendWelcomeEmail($user);
return response()->json(['success' => true]);
}
public function sendOrderNotifications($orderId)
{
$order = \App\Models\Order::find($orderId);
Notification::sendOrderConfirmation($order);
if ($order->status === 'shipped') {
Notification::sendShippingUpdate($order, 'shipped');
}
return response()->json(['success' => true]);
}
}
// Good
Cart::addItem($productId);
Cart::getItemCount();
Cart::calculateTotal();
// Avoid
Cart::add($productId);
Cart::count();
Cart::total();
class AnalyticsService
{
public function trackEvent($category, $action)
{
// tracking logic
return $this;
}
public function setUser($userId)
{
// set user
return $this;
}
}
// Enables chaining
Analytics::setUser('123')->trackEvent('purchase', 'completed');
/**
* @method static \App\Services\CartService addItem(int $productId, int $quantity = 1)
* @method static \App\Services\CartService removeItem(int $productId)
* @method static array getItems()
* @method static float getTotal()
*
* @see \App\Services\CartService
*/
class Cart extends Facade
{
// ...
}
// In Service Provider
$this->app->singleton('cart', function ($app) {
return new CartService($app->make('session'));
});
// Laravel caches resolved facade instances
// First call resolves from container
$instance1 = Cache::getFacadeRoot();
// Subsequent calls use cached instance
$instance2 = Cache::getFacadeRoot();
// $instance1 and $instance2 are the same object
// Facades only resolve when actually used
class HeavyServiceFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'heavy-service'; // Only resolved when called
}
}
You've now mastered Laravel Facades and understand the magic behind their static interface! In our next post, we'll explore Laravel Artisan Commands: Beyond the Basics - Creating Custom Commands to learn how to build powerful command-line tools.