PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
135: The Facade Pattern in PHP
I was working on a project recently where we had to implement a checkout process. It seemed simple enough at first: take the money, check the stock, and send an email. But as the project grew, that "simple" process turned into a nightmare of dependencies. I wanted to show you this because it's the perfect way to understand why we use the Facade pattern.
Dealing with the sprawl
Here is how my code looked initially. I was instantiating everything right in my controller. It was a mess.
<?php
// These are our complex subsystem classes
class InventoryManager {
public function checkStock($productId) { return "Stock checked for $productId"; }
public function reserveItem($productId) { return "Item $productId reserved"; }
}
class PaymentGateway {
public function authorize($amount) { return "Payment of $$amount authorized"; }
public function capture($amount) { return "Payment of $$amount captured"; }
}
class ShippingCarrier {
public function createLabel($address) { return "Label created for $address"; }
public function schedulePickup() { return "Pickup scheduled"; }
}
class NotificationService {
public function sendEmail($email, $msg) { return "Email sent to $email: $msg"; }
}
// The "messy" way of doing things
$inventory = new InventoryManager();
$payment = new PaymentGateway();
$shipping = new ShippingCarrier();
$notifications = new NotificationService();
echo $inventory->checkStock('PHP-BOOK-123') . "\n";
echo $inventory->reserveItem('PHP-BOOK-123') . "\n";
echo $payment->authorize(29.99) . "\n";
echo $payment->capture(29.99) . "\n";
echo $shipping->createLabel('123 Dev Lane') . "\n";
echo $shipping->schedulePickup() . "\n";
echo $notifications->sendEmail('user@example.com', 'Your order is on the way!') . "\n";
?>>
If you look at that, it works. But think about what happens if I want to change the payment gateway to a different provider, or if the ShippingCarrier class adds a new required parameter to createLabel(). I'd have to hunt down every single place in my application where I've manually orchestrated this sequence and fix it. It's brittle. I'm basically forcing my controller to know exactly how the internal plumbing of the warehouse works.
Simplifying the interface
I realized I didn't actually care about the individual steps; I just wanted to "Place an Order." So, I decided to wrap all that complexity inside a single class. This is the essence of the Facade pattern: providing a simplified interface to a complex set of classes.
<?php
class OrderFacade {
private $inventory;
private $payment;
private $shipping;
private $notifications;
public function __construct() {
// The Facade manages the subsystem instances
$this->inventory = new InventoryManager();
$this->payment = new PaymentGateway();
$this->shipping = new ShippingCarrier();
$this->notifications = new NotificationService();
}
public function placeOrder($productId, $amount, $address, $email) {
echo $this->inventory->checkStock($productId) . "\n";
echo $this->inventory->reserveItem($productId) . "\n";
echo $this->payment->authorize($amount) . "\n";
echo $this->payment->capture($amount) . "\n";
echo $this->shipping->createLabel($address) . "\n";
echo $this->shipping->schedulePickup() . "\n";
echo $this->notifications->sendEmail($email, 'Your order is on the way!') . "\n";
return "Order completed successfully!";
}
}
// Now the client code is blissfully ignorant of the complexity
$orderSystem = new OrderFacade();
echo $orderSystem->placeOrder('PHP-BOOK-123', 29.99, '123 Dev Lane', 'user@example.com');
?>>
Notice how much cleaner the calling code is. I've gone from seven lines of orchestration down to one. I've hidden the "how" and only exposed the "what".
The trade-off and the 'Ah-ha!' moment
Now, you might be thinking, "Wait, didn't I just move the mess from the controller into a different class?" In a way, yes. But here is the crucial difference: we've decoupled the client from the subsystems.
If we decide to swap out the PaymentGateway for Stripe or PayPal, I only have to change one line inside the OrderFacade. The rest of my application—the controllers, the API endpoints, the CLI tools—doesn't even know a change happened. They just keep calling placeOrder().
A Facade isn't meant to replace your business logic; it's meant to act as a "front door." It prevents your higher-level code from becoming tightly coupled to the specific implementation details of your low-level services. It's about creating a boundary that keeps the complexity from leaking into the rest of your app.
📋 Practical Task
Build a Cloud Backup Facade
You are building a system that backs up files to the cloud. To do this, your system uses three complex subsystems: FileCompressor, CloudStorageApi, and BackupLogger.
Requirements:
- Create the three subsystem classes:
FileCompressorwith a methodcompress($file).CloudStorageApiwith methodsconnect()andupload($file).BackupLoggerwith a methodlog($message).
- Create a
CloudBackupFacadeclass that simplifies this process into a single method:backup($file). - Inside the
backup()` method, the Facade should orchestrate the following:- Compress the file.
- Connect to the API.
- Upload the compressed file.
- Log the success message.
- Instantiate the
CloudBackupFacadeand call thebackup('my_database.sql')method to verify it works.
There are no comments for now.