- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
1138 lines
27 KiB
Markdown
1138 lines
27 KiB
Markdown
# Entwurfsmuster
|
|
|
|
Diese Dokumentation beschreibt die wichtigsten Entwurfsmuster, die im Framework verwendet werden. Das Verständnis dieser Muster hilft bei der effektiven Nutzung des Frameworks und bei der Erweiterung seiner Funktionalität.
|
|
|
|
## Strukturelle Muster
|
|
|
|
### Adapter
|
|
|
|
Das Adapter-Muster wird verwendet, um Schnittstellen von inkompatiblen Klassen anzupassen, sodass sie zusammenarbeiten können.
|
|
|
|
**Verwendung im Framework:**
|
|
- Datenbank-Adapter für verschiedene Datenbanktypen
|
|
- Cache-Adapter für verschiedene Cache-Backends
|
|
- Logging-Adapter für verschiedene Logging-Systeme
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Interface für die Zielschnittstelle
|
|
interface CacheInterface
|
|
{
|
|
public function get(string $key, mixed $default = null): mixed;
|
|
public function set(string $key, mixed $value, int $ttl = null): bool;
|
|
public function delete(string $key): bool;
|
|
}
|
|
|
|
// Adapter für Redis
|
|
class RedisCacheAdapter implements CacheInterface
|
|
{
|
|
private $redis;
|
|
|
|
public function __construct(Redis $redis)
|
|
{
|
|
$this->redis = $redis;
|
|
}
|
|
|
|
public function get(string $key, mixed $default = null): mixed
|
|
{
|
|
$value = $this->redis->get($key);
|
|
return $value !== false ? unserialize($value) : $default;
|
|
}
|
|
|
|
public function set(string $key, mixed $value, int $ttl = null): bool
|
|
{
|
|
return $this->redis->set($key, serialize($value), $ttl);
|
|
}
|
|
|
|
public function delete(string $key): bool
|
|
{
|
|
return $this->redis->del($key) > 0;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Composite
|
|
|
|
Das Composite-Muster wird verwendet, um Objekte in Baumstrukturen zu organisieren und Einzelobjekte und Kompositionen einheitlich zu behandeln.
|
|
|
|
**Verwendung im Framework:**
|
|
- Menüsystem mit verschachtelten Menüpunkten
|
|
- Validierungsregeln mit komplexen Bedingungen
|
|
- Routengruppen mit verschachtelten Routen
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Basiskomponente
|
|
interface MenuItemInterface
|
|
{
|
|
public function render(): string;
|
|
}
|
|
|
|
// Blattkomponente
|
|
class MenuItem implements MenuItemInterface
|
|
{
|
|
private string $label;
|
|
private string $url;
|
|
|
|
public function __construct(string $label, string $url)
|
|
{
|
|
$this->label = $label;
|
|
$this->url = $url;
|
|
}
|
|
|
|
public function render(): string
|
|
{
|
|
return '<li><a href="' . $this->url . '">' . $this->label . '</a></li>';
|
|
}
|
|
}
|
|
|
|
// Kompositkomponente
|
|
class MenuGroup implements MenuItemInterface
|
|
{
|
|
private string $label;
|
|
private array $items = [];
|
|
|
|
public function __construct(string $label)
|
|
{
|
|
$this->label = $label;
|
|
}
|
|
|
|
public function addItem(MenuItemInterface $item): void
|
|
{
|
|
$this->items[] = $item;
|
|
}
|
|
|
|
public function render(): string
|
|
{
|
|
$output = '<li>' . $this->label . '<ul>';
|
|
foreach ($this->items as $item) {
|
|
$output .= $item->render();
|
|
}
|
|
$output .= '</ul></li>';
|
|
return $output;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Decorator
|
|
|
|
Das Decorator-Muster wird verwendet, um Objekten dynamisch zusätzliche Verantwortlichkeiten hinzuzufügen, ohne ihre Struktur zu ändern.
|
|
|
|
**Verwendung im Framework:**
|
|
- HTTP-Middleware-Stack
|
|
- Caching-Dekoratoren für Repository-Klassen
|
|
- Logging-Dekoratoren für Service-Klassen
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Basiskomponente
|
|
interface RepositoryInterface
|
|
{
|
|
public function find(int $id): ?Model;
|
|
public function findAll(): array;
|
|
}
|
|
|
|
// Konkrete Komponente
|
|
class UserRepository implements RepositoryInterface
|
|
{
|
|
public function find(int $id): ?Model
|
|
{
|
|
// Benutzer aus der Datenbank abrufen
|
|
}
|
|
|
|
public function findAll(): array
|
|
{
|
|
// Alle Benutzer aus der Datenbank abrufen
|
|
}
|
|
}
|
|
|
|
// Basisdekorator
|
|
abstract class RepositoryDecorator implements RepositoryInterface
|
|
{
|
|
protected RepositoryInterface $repository;
|
|
|
|
public function __construct(RepositoryInterface $repository)
|
|
{
|
|
$this->repository = $repository;
|
|
}
|
|
|
|
public function find(int $id): ?Model
|
|
{
|
|
return $this->repository->find($id);
|
|
}
|
|
|
|
public function findAll(): array
|
|
{
|
|
return $this->repository->findAll();
|
|
}
|
|
}
|
|
|
|
// Konkreter Dekorator
|
|
class CachingRepositoryDecorator extends RepositoryDecorator
|
|
{
|
|
private CacheInterface $cache;
|
|
|
|
public function __construct(RepositoryInterface $repository, CacheInterface $cache)
|
|
{
|
|
parent::__construct($repository);
|
|
$this->cache = $cache;
|
|
}
|
|
|
|
public function find(int $id): ?Model
|
|
{
|
|
$cacheKey = 'user:' . $id;
|
|
if ($this->cache->has($cacheKey)) {
|
|
return $this->cache->get($cacheKey);
|
|
}
|
|
|
|
$user = $this->repository->find($id);
|
|
if ($user) {
|
|
$this->cache->set($cacheKey, $user, 3600);
|
|
}
|
|
|
|
return $user;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Facade
|
|
|
|
Das Facade-Muster bietet eine vereinfachte Schnittstelle zu einem komplexen Subsystem.
|
|
|
|
**Verwendung im Framework:**
|
|
- Datenbank-Facade für einfachen Zugriff auf Datenbankoperationen
|
|
- Cache-Facade für einfachen Zugriff auf Cache-Operationen
|
|
- Auth-Facade für einfachen Zugriff auf Authentifizierungsfunktionen
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Komplexes Subsystem
|
|
class Authentication
|
|
{
|
|
public function checkCredentials(string $username, string $password): bool
|
|
{
|
|
// Überprüfen der Anmeldeinformationen
|
|
}
|
|
}
|
|
|
|
class Session
|
|
{
|
|
public function set(string $key, mixed $value): void
|
|
{
|
|
// Wert in der Sitzung speichern
|
|
}
|
|
|
|
public function get(string $key): mixed
|
|
{
|
|
// Wert aus der Sitzung abrufen
|
|
}
|
|
}
|
|
|
|
class User
|
|
{
|
|
public function findByUsername(string $username): ?User
|
|
{
|
|
// Benutzer nach Benutzernamen suchen
|
|
}
|
|
}
|
|
|
|
// Facade
|
|
class Auth
|
|
{
|
|
private Authentication $authentication;
|
|
private Session $session;
|
|
private User $user;
|
|
|
|
public function __construct(Authentication $authentication, Session $session, User $user)
|
|
{
|
|
$this->authentication = $authentication;
|
|
$this->session = $session;
|
|
$this->user = $user;
|
|
}
|
|
|
|
public function login(string $username, string $password): bool
|
|
{
|
|
if ($this->authentication->checkCredentials($username, $password)) {
|
|
$user = $this->user->findByUsername($username);
|
|
$this->session->set('user_id', $user->id);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function logout(): void
|
|
{
|
|
$this->session->set('user_id', null);
|
|
}
|
|
|
|
public function check(): bool
|
|
{
|
|
return $this->session->get('user_id') !== null;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Proxy
|
|
|
|
Das Proxy-Muster stellt einen Platzhalter für ein anderes Objekt bereit, um den Zugriff darauf zu kontrollieren.
|
|
|
|
**Verwendung im Framework:**
|
|
- Lazy-Loading-Proxies für ressourcenintensive Objekte
|
|
- Remote-Proxies für API-Aufrufe
|
|
- Zugriffssteuerung-Proxies für Autorisierung
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Subjektschnittstelle
|
|
interface ImageInterface
|
|
{
|
|
public function display(): void;
|
|
}
|
|
|
|
// Reales Subjekt
|
|
class Image implements ImageInterface
|
|
{
|
|
private string $filename;
|
|
|
|
public function __construct(string $filename)
|
|
{
|
|
$this->filename = $filename;
|
|
$this->loadImageFromDisk(); // Teure Operation
|
|
}
|
|
|
|
private function loadImageFromDisk(): void
|
|
{
|
|
echo "Loading image {$this->filename} from disk...\n";
|
|
// Bild laden
|
|
}
|
|
|
|
public function display(): void
|
|
{
|
|
echo "Displaying image {$this->filename}\n";
|
|
}
|
|
}
|
|
|
|
// Proxy
|
|
class LazyImage implements ImageInterface
|
|
{
|
|
private ?Image $realImage = null;
|
|
private string $filename;
|
|
|
|
public function __construct(string $filename)
|
|
{
|
|
$this->filename = $filename;
|
|
}
|
|
|
|
public function display(): void
|
|
{
|
|
if ($this->realImage === null) {
|
|
$this->realImage = new Image($this->filename);
|
|
}
|
|
|
|
$this->realImage->display();
|
|
}
|
|
}
|
|
```
|
|
|
|
## Verhaltensmuster
|
|
|
|
### Chain of Responsibility
|
|
|
|
Das Chain of Responsibility-Muster ermöglicht es, eine Anfrage entlang einer Kette von Handlern zu senden, wobei jeder Handler entscheidet, ob er die Anfrage verarbeitet oder an den nächsten Handler weiterleitet.
|
|
|
|
**Verwendung im Framework:**
|
|
- Middleware-Pipeline für HTTP-Anfragen
|
|
- Ereignislistener-Kette
|
|
- Validierungskette
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Handler-Schnittstelle
|
|
interface RequestHandlerInterface
|
|
{
|
|
public function handle(Request $request): Response;
|
|
}
|
|
|
|
// Konkreter Handler
|
|
class AuthenticationMiddleware implements RequestHandlerInterface
|
|
{
|
|
private RequestHandlerInterface $next;
|
|
|
|
public function setNext(RequestHandlerInterface $next): void
|
|
{
|
|
$this->next = $next;
|
|
}
|
|
|
|
public function handle(Request $request): Response
|
|
{
|
|
if (!$this->isAuthenticated($request)) {
|
|
return new Response('Unauthorized', 401);
|
|
}
|
|
|
|
return $this->next->handle($request);
|
|
}
|
|
|
|
private function isAuthenticated(Request $request): bool
|
|
{
|
|
// Überprüfen, ob der Benutzer authentifiziert ist
|
|
}
|
|
}
|
|
|
|
// Konkreter Handler
|
|
class AuthorizationMiddleware implements RequestHandlerInterface
|
|
{
|
|
private RequestHandlerInterface $next;
|
|
|
|
public function setNext(RequestHandlerInterface $next): void
|
|
{
|
|
$this->next = $next;
|
|
}
|
|
|
|
public function handle(Request $request): Response
|
|
{
|
|
if (!$this->isAuthorized($request)) {
|
|
return new Response('Forbidden', 403);
|
|
}
|
|
|
|
return $this->next->handle($request);
|
|
}
|
|
|
|
private function isAuthorized(Request $request): bool
|
|
{
|
|
// Überprüfen, ob der Benutzer autorisiert ist
|
|
}
|
|
}
|
|
```
|
|
|
|
### Command
|
|
|
|
Das Command-Muster kapselt eine Anfrage als Objekt, wodurch es möglich wird, Anfragen zu parametrisieren, in eine Warteschlange zu stellen, zu protokollieren und rückgängig zu machen.
|
|
|
|
**Verwendung im Framework:**
|
|
- Konsolenbefehle
|
|
- Aufgaben in der Warteschlange
|
|
- Transaktionen
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Befehlsschnittstelle
|
|
interface CommandInterface
|
|
{
|
|
public function execute(): void;
|
|
}
|
|
|
|
// Konkreter Befehl
|
|
class CreateUserCommand implements CommandInterface
|
|
{
|
|
private UserRepository $userRepository;
|
|
private string $name;
|
|
private string $email;
|
|
|
|
public function __construct(UserRepository $userRepository, string $name, string $email)
|
|
{
|
|
$this->userRepository = $userRepository;
|
|
$this->name = $name;
|
|
$this->email = $email;
|
|
}
|
|
|
|
public function execute(): void
|
|
{
|
|
$user = new User();
|
|
$user->name = $this->name;
|
|
$user->email = $this->email;
|
|
$this->userRepository->save($user);
|
|
}
|
|
}
|
|
|
|
// Aufrufer
|
|
class CommandBus
|
|
{
|
|
public function dispatch(CommandInterface $command): void
|
|
{
|
|
$command->execute();
|
|
}
|
|
}
|
|
```
|
|
|
|
### Observer
|
|
|
|
Das Observer-Muster definiert eine Eins-zu-viele-Abhängigkeit zwischen Objekten, sodass alle abhängigen Objekte automatisch benachrichtigt und aktualisiert werden, wenn ein Objekt seinen Zustand ändert.
|
|
|
|
**Verwendung im Framework:**
|
|
- Ereignissystem
|
|
- Modell-Beobachter
|
|
- Konfigurationsänderungen
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Subjektschnittstelle
|
|
interface SubjectInterface
|
|
{
|
|
public function attach(ObserverInterface $observer): void;
|
|
public function detach(ObserverInterface $observer): void;
|
|
public function notify(): void;
|
|
}
|
|
|
|
// Beobachterschnittstelle
|
|
interface ObserverInterface
|
|
{
|
|
public function update(SubjectInterface $subject): void;
|
|
}
|
|
|
|
// Konkretes Subjekt
|
|
class User implements SubjectInterface
|
|
{
|
|
private array $observers = [];
|
|
private string $name;
|
|
private string $email;
|
|
|
|
public function attach(ObserverInterface $observer): void
|
|
{
|
|
$this->observers[] = $observer;
|
|
}
|
|
|
|
public function detach(ObserverInterface $observer): void
|
|
{
|
|
$index = array_search($observer, $this->observers, true);
|
|
if ($index !== false) {
|
|
unset($this->observers[$index]);
|
|
}
|
|
}
|
|
|
|
public function notify(): void
|
|
{
|
|
foreach ($this->observers as $observer) {
|
|
$observer->update($this);
|
|
}
|
|
}
|
|
|
|
public function setName(string $name): void
|
|
{
|
|
$this->name = $name;
|
|
$this->notify();
|
|
}
|
|
|
|
public function setEmail(string $email): void
|
|
{
|
|
$this->email = $email;
|
|
$this->notify();
|
|
}
|
|
|
|
public function getName(): string
|
|
{
|
|
return $this->name;
|
|
}
|
|
|
|
public function getEmail(): string
|
|
{
|
|
return $this->email;
|
|
}
|
|
}
|
|
|
|
// Konkreter Beobachter
|
|
class UserLogger implements ObserverInterface
|
|
{
|
|
public function update(SubjectInterface $subject): void
|
|
{
|
|
if ($subject instanceof User) {
|
|
echo "User updated: {$subject->getName()} ({$subject->getEmail()})\n";
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Strategy
|
|
|
|
Das Strategy-Muster definiert eine Familie von Algorithmen, kapselt jeden einzelnen und macht sie austauschbar. Das Muster ermöglicht es, den Algorithmus unabhängig von den Clients zu variieren, die ihn verwenden.
|
|
|
|
**Verwendung im Framework:**
|
|
- Validierungsstrategien
|
|
- Caching-Strategien
|
|
- Authentifizierungsstrategien
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Strategieschnittstelle
|
|
interface PaymentStrategyInterface
|
|
{
|
|
public function pay(float $amount): bool;
|
|
}
|
|
|
|
// Konkrete Strategien
|
|
class CreditCardPaymentStrategy implements PaymentStrategyInterface
|
|
{
|
|
private string $cardNumber;
|
|
private string $expiryDate;
|
|
private string $cvv;
|
|
|
|
public function __construct(string $cardNumber, string $expiryDate, string $cvv)
|
|
{
|
|
$this->cardNumber = $cardNumber;
|
|
$this->expiryDate = $expiryDate;
|
|
$this->cvv = $cvv;
|
|
}
|
|
|
|
public function pay(float $amount): bool
|
|
{
|
|
// Kreditkartenzahlung verarbeiten
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class PayPalPaymentStrategy implements PaymentStrategyInterface
|
|
{
|
|
private string $email;
|
|
private string $password;
|
|
|
|
public function __construct(string $email, string $password)
|
|
{
|
|
$this->email = $email;
|
|
$this->password = $password;
|
|
}
|
|
|
|
public function pay(float $amount): bool
|
|
{
|
|
// PayPal-Zahlung verarbeiten
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Kontext
|
|
class PaymentProcessor
|
|
{
|
|
private PaymentStrategyInterface $paymentStrategy;
|
|
|
|
public function setPaymentStrategy(PaymentStrategyInterface $paymentStrategy): void
|
|
{
|
|
$this->paymentStrategy = $paymentStrategy;
|
|
}
|
|
|
|
public function processPayment(float $amount): bool
|
|
{
|
|
return $this->paymentStrategy->pay($amount);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Template Method
|
|
|
|
Das Template Method-Muster definiert das Skelett eines Algorithmus in einer Methode und verschiebt einige Schritte in Unterklassen. Das Muster ermöglicht es Unterklassen, bestimmte Schritte eines Algorithmus zu überschreiben, ohne die Struktur des Algorithmus zu ändern.
|
|
|
|
**Verwendung im Framework:**
|
|
- Controller-Basisklasse mit Hooks
|
|
- Migrationsprozess
|
|
- Testfälle
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Abstrakte Klasse mit Template-Methode
|
|
abstract class Report
|
|
{
|
|
// Template-Methode
|
|
public function generate(): string
|
|
{
|
|
$data = $this->fetchData();
|
|
$processedData = $this->processData($data);
|
|
$report = $this->formatReport($processedData);
|
|
$this->saveReport($report);
|
|
return $report;
|
|
}
|
|
|
|
// Abstrakte Methoden, die von Unterklassen implementiert werden müssen
|
|
abstract protected function fetchData(): array;
|
|
abstract protected function formatReport(array $data): string;
|
|
|
|
// Konkrete Methoden, die von Unterklassen überschrieben werden können
|
|
protected function processData(array $data): array
|
|
{
|
|
// Standardverarbeitung
|
|
return $data;
|
|
}
|
|
|
|
protected function saveReport(string $report): void
|
|
{
|
|
// Standardspeicherung
|
|
file_put_contents('report.txt', $report);
|
|
}
|
|
}
|
|
|
|
// Konkrete Unterklasse
|
|
class SalesReport extends Report
|
|
{
|
|
protected function fetchData(): array
|
|
{
|
|
// Verkaufsdaten abrufen
|
|
return [
|
|
['product' => 'Product A', 'sales' => 100],
|
|
['product' => 'Product B', 'sales' => 200],
|
|
];
|
|
}
|
|
|
|
protected function processData(array $data): array
|
|
{
|
|
// Spezielle Verarbeitung für Verkaufsdaten
|
|
$processedData = [];
|
|
$totalSales = 0;
|
|
|
|
foreach ($data as $item) {
|
|
$totalSales += $item['sales'];
|
|
$processedData[] = $item;
|
|
}
|
|
|
|
$processedData[] = ['product' => 'Total', 'sales' => $totalSales];
|
|
|
|
return $processedData;
|
|
}
|
|
|
|
protected function formatReport(array $data): string
|
|
{
|
|
// Formatierung als CSV
|
|
$report = "Product,Sales\n";
|
|
|
|
foreach ($data as $item) {
|
|
$report .= "{$item['product']},{$item['sales']}\n";
|
|
}
|
|
|
|
return $report;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Erzeugungsmuster
|
|
|
|
### Factory Method
|
|
|
|
Das Factory Method-Muster definiert eine Schnittstelle zum Erstellen eines Objekts, lässt aber Unterklassen entscheiden, welche Klasse instanziiert werden soll. Das Muster ermöglicht es einer Klasse, die Instanziierung an Unterklassen zu delegieren.
|
|
|
|
**Verwendung im Framework:**
|
|
- Controller-Factory
|
|
- Repository-Factory
|
|
- Service-Factory
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Produktschnittstelle
|
|
interface LoggerInterface
|
|
{
|
|
public function log(string $message): void;
|
|
}
|
|
|
|
// Konkrete Produkte
|
|
class FileLogger implements LoggerInterface
|
|
{
|
|
private string $filePath;
|
|
|
|
public function __construct(string $filePath)
|
|
{
|
|
$this->filePath = $filePath;
|
|
}
|
|
|
|
public function log(string $message): void
|
|
{
|
|
file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND);
|
|
}
|
|
}
|
|
|
|
class DatabaseLogger implements LoggerInterface
|
|
{
|
|
private PDO $pdo;
|
|
|
|
public function __construct(PDO $pdo)
|
|
{
|
|
$this->pdo = $pdo;
|
|
}
|
|
|
|
public function log(string $message): void
|
|
{
|
|
$stmt = $this->pdo->prepare('INSERT INTO logs (message) VALUES (:message)');
|
|
$stmt->execute(['message' => $message]);
|
|
}
|
|
}
|
|
|
|
// Creator-Schnittstelle mit Factory-Methode
|
|
interface LoggerFactoryInterface
|
|
{
|
|
public function createLogger(): LoggerInterface;
|
|
}
|
|
|
|
// Konkrete Creators
|
|
class FileLoggerFactory implements LoggerFactoryInterface
|
|
{
|
|
private string $filePath;
|
|
|
|
public function __construct(string $filePath)
|
|
{
|
|
$this->filePath = $filePath;
|
|
}
|
|
|
|
public function createLogger(): LoggerInterface
|
|
{
|
|
return new FileLogger($this->filePath);
|
|
}
|
|
}
|
|
|
|
class DatabaseLoggerFactory implements LoggerFactoryInterface
|
|
{
|
|
private PDO $pdo;
|
|
|
|
public function __construct(PDO $pdo)
|
|
{
|
|
$this->pdo = $pdo;
|
|
}
|
|
|
|
public function createLogger(): LoggerInterface
|
|
{
|
|
return new DatabaseLogger($this->pdo);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Abstract Factory
|
|
|
|
Das Abstract Factory-Muster bietet eine Schnittstelle zum Erstellen von Familien verwandter oder abhängiger Objekte, ohne ihre konkreten Klassen zu spezifizieren.
|
|
|
|
**Verwendung im Framework:**
|
|
- UI-Komponenten-Factory
|
|
- Datenbankabstraktions-Factory
|
|
- Service-Factory für verschiedene Umgebungen
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Abstrakte Produktschnittstellen
|
|
interface ButtonInterface
|
|
{
|
|
public function render(): string;
|
|
}
|
|
|
|
interface InputInterface
|
|
{
|
|
public function render(): string;
|
|
}
|
|
|
|
// Konkrete Produkte für Bootstrap
|
|
class BootstrapButton implements ButtonInterface
|
|
{
|
|
private string $label;
|
|
|
|
public function __construct(string $label)
|
|
{
|
|
$this->label = $label;
|
|
}
|
|
|
|
public function render(): string
|
|
{
|
|
return '<button class="btn btn-primary">' . $this->label . '</button>';
|
|
}
|
|
}
|
|
|
|
class BootstrapInput implements InputInterface
|
|
{
|
|
private string $name;
|
|
private string $placeholder;
|
|
|
|
public function __construct(string $name, string $placeholder)
|
|
{
|
|
$this->name = $name;
|
|
$this->placeholder = $placeholder;
|
|
}
|
|
|
|
public function render(): string
|
|
{
|
|
return '<input type="text" class="form-control" name="' . $this->name . '" placeholder="' . $this->placeholder . '">';
|
|
}
|
|
}
|
|
|
|
// Konkrete Produkte für Material Design
|
|
class MaterialButton implements ButtonInterface
|
|
{
|
|
private string $label;
|
|
|
|
public function __construct(string $label)
|
|
{
|
|
$this->label = $label;
|
|
}
|
|
|
|
public function render(): string
|
|
{
|
|
return '<button class="mdc-button mdc-button--raised">' . $this->label . '</button>';
|
|
}
|
|
}
|
|
|
|
class MaterialInput implements InputInterface
|
|
{
|
|
private string $name;
|
|
private string $placeholder;
|
|
|
|
public function __construct(string $name, string $placeholder)
|
|
{
|
|
$this->name = $name;
|
|
$this->placeholder = $placeholder;
|
|
}
|
|
|
|
public function render(): string
|
|
{
|
|
return '<div class="mdc-text-field"><input type="text" name="' . $this->name . '" placeholder="' . $this->placeholder . '" class="mdc-text-field__input"></div>';
|
|
}
|
|
}
|
|
|
|
// Abstrakte Factory-Schnittstelle
|
|
interface UIFactoryInterface
|
|
{
|
|
public function createButton(string $label): ButtonInterface;
|
|
public function createInput(string $name, string $placeholder): InputInterface;
|
|
}
|
|
|
|
// Konkrete Factories
|
|
class BootstrapUIFactory implements UIFactoryInterface
|
|
{
|
|
public function createButton(string $label): ButtonInterface
|
|
{
|
|
return new BootstrapButton($label);
|
|
}
|
|
|
|
public function createInput(string $name, string $placeholder): InputInterface
|
|
{
|
|
return new BootstrapInput($name, $placeholder);
|
|
}
|
|
}
|
|
|
|
class MaterialUIFactory implements UIFactoryInterface
|
|
{
|
|
public function createButton(string $label): ButtonInterface
|
|
{
|
|
return new MaterialButton($label);
|
|
}
|
|
|
|
public function createInput(string $name, string $placeholder): InputInterface
|
|
{
|
|
return new MaterialInput($name, $placeholder);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Singleton
|
|
|
|
Das Singleton-Muster stellt sicher, dass eine Klasse nur eine Instanz hat und bietet einen globalen Zugriffspunkt auf diese Instanz.
|
|
|
|
**Verwendung im Framework:**
|
|
- Konfigurationsmanager
|
|
- Datenbankverbindung
|
|
- Logger
|
|
|
|
**Beispiel:**
|
|
```php
|
|
class DatabaseConnection
|
|
{
|
|
private static ?DatabaseConnection $instance = null;
|
|
private PDO $pdo;
|
|
|
|
private function __construct(string $dsn, string $username, string $password)
|
|
{
|
|
$this->pdo = new PDO($dsn, $username, $password);
|
|
}
|
|
|
|
public static function getInstance(string $dsn, string $username, string $password): DatabaseConnection
|
|
{
|
|
if (self::$instance === null) {
|
|
self::$instance = new self($dsn, $username, $password);
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
public function getConnection(): PDO
|
|
{
|
|
return $this->pdo;
|
|
}
|
|
|
|
// Verhindern des Klonens
|
|
private function __clone() {}
|
|
|
|
// Verhindern der Deserialisierung
|
|
public function __wakeup()
|
|
{
|
|
throw new \Exception("Cannot unserialize a singleton.");
|
|
}
|
|
}
|
|
```
|
|
|
|
### Builder
|
|
|
|
Das Builder-Muster trennt die Konstruktion eines komplexen Objekts von seiner Repräsentation, sodass derselbe Konstruktionsprozess verschiedene Repräsentationen erstellen kann.
|
|
|
|
**Verwendung im Framework:**
|
|
- Query Builder
|
|
- Form Builder
|
|
- Response Builder
|
|
|
|
**Beispiel:**
|
|
```php
|
|
// Produkt
|
|
class Query
|
|
{
|
|
private string $select = '*';
|
|
private string $from = '';
|
|
private array $where = [];
|
|
private array $orderBy = [];
|
|
private ?int $limit = null;
|
|
private ?int $offset = null;
|
|
|
|
public function setSelect(string $select): void
|
|
{
|
|
$this->select = $select;
|
|
}
|
|
|
|
public function setFrom(string $from): void
|
|
{
|
|
$this->from = $from;
|
|
}
|
|
|
|
public function addWhere(string $condition): void
|
|
{
|
|
$this->where[] = $condition;
|
|
}
|
|
|
|
public function addOrderBy(string $column, string $direction = 'ASC'): void
|
|
{
|
|
$this->orderBy[] = "$column $direction";
|
|
}
|
|
|
|
public function setLimit(?int $limit): void
|
|
{
|
|
$this->limit = $limit;
|
|
}
|
|
|
|
public function setOffset(?int $offset): void
|
|
{
|
|
$this->offset = $offset;
|
|
}
|
|
|
|
public function getSql(): string
|
|
{
|
|
$sql = "SELECT {$this->select} FROM {$this->from}";
|
|
|
|
if (!empty($this->where)) {
|
|
$sql .= " WHERE " . implode(' AND ', $this->where);
|
|
}
|
|
|
|
if (!empty($this->orderBy)) {
|
|
$sql .= " ORDER BY " . implode(', ', $this->orderBy);
|
|
}
|
|
|
|
if ($this->limit !== null) {
|
|
$sql .= " LIMIT {$this->limit}";
|
|
}
|
|
|
|
if ($this->offset !== null) {
|
|
$sql .= " OFFSET {$this->offset}";
|
|
}
|
|
|
|
return $sql;
|
|
}
|
|
}
|
|
|
|
// Builder-Schnittstelle
|
|
interface QueryBuilderInterface
|
|
{
|
|
public function select(string $columns): QueryBuilderInterface;
|
|
public function from(string $table): QueryBuilderInterface;
|
|
public function where(string $condition): QueryBuilderInterface;
|
|
public function orderBy(string $column, string $direction = 'ASC'): QueryBuilderInterface;
|
|
public function limit(int $limit): QueryBuilderInterface;
|
|
public function offset(int $offset): QueryBuilderInterface;
|
|
public function getQuery(): Query;
|
|
}
|
|
|
|
// Konkreter Builder
|
|
class QueryBuilder implements QueryBuilderInterface
|
|
{
|
|
private Query $query;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->query = new Query();
|
|
}
|
|
|
|
public function select(string $columns): QueryBuilderInterface
|
|
{
|
|
$this->query->setSelect($columns);
|
|
return $this;
|
|
}
|
|
|
|
public function from(string $table): QueryBuilderInterface
|
|
{
|
|
$this->query->setFrom($table);
|
|
return $this;
|
|
}
|
|
|
|
public function where(string $condition): QueryBuilderInterface
|
|
{
|
|
$this->query->addWhere($condition);
|
|
return $this;
|
|
}
|
|
|
|
public function orderBy(string $column, string $direction = 'ASC'): QueryBuilderInterface
|
|
{
|
|
$this->query->addOrderBy($column, $direction);
|
|
return $this;
|
|
}
|
|
|
|
public function limit(int $limit): QueryBuilderInterface
|
|
{
|
|
$this->query->setLimit($limit);
|
|
return $this;
|
|
}
|
|
|
|
public function offset(int $offset): QueryBuilderInterface
|
|
{
|
|
$this->query->setOffset($offset);
|
|
return $this;
|
|
}
|
|
|
|
public function getQuery(): Query
|
|
{
|
|
return $this->query;
|
|
}
|
|
}
|
|
|
|
// Director
|
|
class QueryDirector
|
|
{
|
|
private QueryBuilderInterface $builder;
|
|
|
|
public function __construct(QueryBuilderInterface $builder)
|
|
{
|
|
$this->builder = $builder;
|
|
}
|
|
|
|
public function buildUserQuery(): Query
|
|
{
|
|
return $this->builder
|
|
->select('id, name, email')
|
|
->from('users')
|
|
->where('active = 1')
|
|
->orderBy('name')
|
|
->limit(10)
|
|
->getQuery();
|
|
}
|
|
|
|
public function buildPostQuery(int $userId): Query
|
|
{
|
|
return $this->builder
|
|
->select('id, title, content')
|
|
->from('posts')
|
|
->where("user_id = $userId")
|
|
->where('published = 1')
|
|
->orderBy('created_at', 'DESC')
|
|
->getQuery();
|
|
}
|
|
}
|
|
```
|
|
|
|
## Weitere Informationen
|
|
|
|
- [Architekturübersicht](overview.md): Überblick über die Architektur des Frameworks.
|
|
- [Komponenten](components.md): Detaillierte Informationen zu den Hauptkomponenten des Frameworks.
|
|
- [Komponentendokumentation](../components/README.md): Dokumentation der einzelnen Komponenten.
|