Why Design Patterns?
Design patterns are reusable solutions to common problems. They give developers a shared vocabulary and prevent reinventing the wheel. Senior PHP developers are expected to know and apply them.
Singleton — One Instance Only
<?php
class Database {
private static ?self $instance = null;
private PDO $pdo;
private function __construct() {
$this->pdo = new PDO('mysql:host=localhost;dbname=app', 'user', 'pass');
}
private function __clone() {}
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function query(string $sql, array $params = []): array {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
$db = Database::getInstance();
$users = $db->query('SELECT * FROM users WHERE active = ?', [1]);
Factory — Create Without new
<?php
interface Notifier {
public function send(string $to, string $message): bool;
}
class EmailNotifier implements Notifier {
public function send(string $to, string $message): bool {
echo "Email to $to: $message\n";
return true;
}
}
class SMSNotifier implements Notifier {
public function send(string $to, string $message): bool {
echo "SMS to $to: $message\n";
return true;
}
}
class NotifierFactory {
public static function create(string $type): Notifier {
return match($type) {
'email' => new EmailNotifier(),
'sms' => new SMSNotifier(),
default => throw new InvalidArgumentException("Unknown: $type"),
};
}
}
$n = NotifierFactory::create(config('notify.default'));
$n->send('rahul@example.com', 'Welcome to EzyCoders!');
Observer — Event-Driven
<?php
interface Observer {
public function update(string $event, mixed $data): void;
}
class EventBus {
private array $listeners = [];
public function on(string $event, Observer $obs): void {
$this->listeners[$event][] = $obs;
}
public function emit(string $event, mixed $data = null): void {
foreach ($this->listeners[$event] ?? [] as $obs) {
$obs->update($event, $data);
}
}
}
class WelcomeEmailer implements Observer {
public function update(string $event, mixed $data): void {
echo "Sending welcome email to {$data['email']}\n";
}
}
class ReputationGranter implements Observer {
public function update(string $event, mixed $data): void {
echo "Granting 50 points to user {$data['id']}\n";
}
}
$bus = new EventBus();
$bus->on('user.registered', new WelcomeEmailer());
$bus->on('user.registered', new ReputationGranter());
$bus->emit('user.registered', ['id'=>1,'email'=>'r@example.com']);
Repository — Abstract Data Access
<?php
interface PostRepository {
public function findById(int $id): ?array;
public function findPublished(): array;
public function save(array $post): int;
}
class MysqlPostRepository implements PostRepository {
public function __construct(private PDO $db) {}
public function findById(int $id): ?array {
$s = $this->db->prepare('SELECT * FROM posts WHERE id=?');
$s->execute([$id]);
return $s->fetch(PDO::FETCH_ASSOC) ?: null;
}
public function findPublished(): array {
return $this->db->query("SELECT * FROM posts WHERE status='published'")->fetchAll(PDO::FETCH_ASSOC);
}
public function save(array $post): int {
$s = $this->db->prepare('INSERT INTO posts (title,content) VALUES (?,?)');
$s->execute([$post['title'], $post['content']]);
return (int) $this->db->lastInsertId();
}
}
Q: What problem does the Repository pattern solve?
It decouples business logic from data access. Your controllers and services call repository methods without knowing whether the data comes from MySQL, MongoDB, an API, or in-memory test data. Swapping databases requires only implementing a new repository class.
Comments (0)
No comments yet. Be the first!
Leave a Comment