Repository-Muster
Teil des Abschnitts Objektorientierte Programmierung der PHP-Journey von Coddy. Lektion 81 von 91.
Das Repository Pattern ist ein Entwurfsmuster, das zwischen der Geschäftslogik deiner Anwendung und der Datenspeicherung vermittelt. Es stellt eine sammlungsähnliche Schnittstelle für den Zugriff auf Domänenobjekte bereit und verbirgt die Details darüber, wie Daten tatsächlich gespeichert oder abgerufen werden.
Stell dir ein Repository als eine spezialisierte Sammlung vor, die weiß, wie Objekte dauerhaft gespeichert und abgerufen werden. Dein Anwendungscode fragt das Repository nach einem Benutzer anhand seiner ID oder speichert ein neues Produkt, ohne zu wissen, ob sich die Daten in einer Datenbank, einer Datei oder einer API befinden. Diese Trennung macht deinen Code besser testbar und flexibler.
<?php
interface UserRepositoryInterface {
public function find(int $id): ?User;
public function findAll(): array;
public function save(User $user): void;
public function delete(int $id): void;
}
class User {
public function __construct(
public int $id,
public string $name,
public string $email
) {}
}
Die Repository-Implementierung übernimmt den eigentlichen Speichermechanismus:
<?php
class InMemoryUserRepository implements UserRepositoryInterface {
private array $users = [];
public function find(int $id): ?User {
return $this->users[$id] ?? null;
}
public function findAll(): array {
return array_values($this->users);
}
public function save(User $user): void {
$this->users[$user->id] = $user;
}
public function delete(int $id): void {
unset($this->users[$id]);
}
}
$repo = new InMemoryUserRepository();
$repo->save(new User(1, "Alice", "alice@example.com"));
$repo->save(new User(2, "Bob", "bob@example.com"));
echo $repo->find(1)->name . "\n";
echo count($repo->findAll());
Ausgabe:
Alice
2Das Schöne an diesem Muster ist, dass du Implementierungen austauschen kannst, ohne deine Geschäftslogik zu ändern. Musst du von der In-Memory-Speicherung zu einer Datenbank wechseln? Erstelle eine neue Repository-Klasse, die dieselbe Schnittstelle implementiert. Dein Anwendungscode bleibt unverändert, weil er von der Schnittstelle und nicht von der konkreten Implementierung abhängt.
Aufgabe
EinfachErstellen wir ein Produktinventarsystem unter Verwendung des Repository-Musters. Du wirst eine klare Trennung zwischen deinen Domänenobjekten und ihrer Speicherung schaffen, sodass dein Anwendungscode mit Produkten arbeiten kann, ohne die Details der Speicherung zu kennen.
Du wirst deinen Code auf vier Dateien verteilen:
Product.php: Erstelle eineProduct-Klasse, die die Artikel in deinem Inventar repräsentiert. Jedes Produkt hat eineid(int), einenname(string) und einenprice(float). Verwende Konstruktor-Promotion mit öffentlichen Eigenschaften für einen einfachen Zugriff.ProductRepositoryInterface.php: Definiere einProductRepositoryInterface, das den Vertrag für jede Implementierung der Produktspeicherung festlegt. Deine Schnittstelle sollte diese Methoden deklarieren:find(int $id): ?Product: Ruft ein Produkt anhand seiner ID ab oder gibt null zurück, wenn es nicht gefunden wurdefindAll(): array: Ruft alle Produkte als Array absave(Product $product): void: Speichert ein Produkt oder aktualisiert esdelete(int $id): void: Entfernt ein Produkt anhand seiner ID
InMemoryProductRepository.php: Binde sowohl die Product-Klasse als auch die Schnittstelle ein. Erstelle eineInMemoryProductRepository-Klasse, die die Schnittstelle implementiert und ein privates Array zum Speichern der Produkte verwendet. Produkte sollten für eine effiziente Suche nach ihrer ID indiziert werden. Die MethodefindAll()sollte nur die Produktobjekte zurückgeben (verwendearray_values(), um die Array-Schlüssel zurückzusetzen).main.php: Binde die Repository-Datei ein. Du erhältst zwei Eingaben: zuerst eine JSON-Zeichenfolge mit hinzuzufügenden Produkten und anschließend in einer separaten Zeile die ganzzahlige ID, nach der gesucht werden soll.Das JSON-Format lautet:
[{"id": 1, "name": "Laptop", "price": 999.99}, {"id": 2, "name": "Mouse", "price": 29.99}]Erstelle ein
InMemoryProductRepository, speichere alle Produkte aus der JSON-Eingabe und verwende anschließend die zweite Eingabe (die ID), um ein bestimmtes Produkt zu finden. Gib die Ergebnisse in diesem Format aus:Total products: [count] Found: [name] - $[price]Formatiere den Preis mit zwei Nachkommastellen. Wenn das Produkt nicht gefunden wird, gib stattdessen
Found: Not foundaus.
Das Repository-Muster zeigt seine Stärken, wenn du die Speicherimplementierungen austauschen musst. Dein Hauptcode hängt nur von der Schnittstelle ab: Der Wechsel von einem In-Memory-Speicher zu einer Datenbank würde bedeuten, eine neue Repository-Klasse zu erstellen, ohne die Anwendungslogik zu ändern.
Probier es selbst
<?php
require_once 'InMemoryProductRepository.php';
// Eingabe lesen
$jsonInput = trim(fgets(STDIN));
$searchId = intval(trim(fgets(STDIN)));
// Die JSON-Eingabe in ein Array parsen
$productsData = (array)json_decode($jsonInput, true);
// TODO: Eine InMemoryProductRepository-Instanz erstellen
// TODO: Durch $productsData iterieren und jedes Produkt speichern
// Jedes Element hat die Schlüssel 'id', 'name' und 'price'
// TODO: Alle Produkte abrufen und die Gesamtanzahl ausgeben
// Format: "Total products: [count]"
// TODO: Das Produkt mit $searchId finden
// Wenn gefunden, ausgeben: "Found: [name] - $[price]" (Preis auf 2 Dezimalstellen formatiert)
// If not found, print: "Found: Not found"
?>Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in OOPKlassen vs. ObjekteDas Schlüsselwort $thisMethodenEigenschaftenKonstruktor (__construct)Destruktor (__destruct)Rückblick – Einfacher Taschenrechner4Vererbung
Grundlegende VererbungDas Schlüsselwort parent::MethodenüberschreibungDas Schlüsselwort finalAbstrakte KlassenRückblick – Mitarbeiterhierarchie7Kapselung
Öffentlich, geschützt, privatZugriffsmodifikatoren im DetailGetter und SetterInformationsverbergungKonstruktor-Promotion (8.0)Wiederholung – System zur Verwaltung von Schülerdatensätzen10Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungDependency InjectionAnonyme KlassenEnums (PHP 8.1)Fibers (PHP 8.1)Objektklonen im DetailGeneratoren & Iteratoren13Entwurfsmuster Teil 2
BefehlsmusterAdaptermusterDekorierermusterSchablonenmethodenmusterZustandsmusterKompositummusterRepository-Muster2Namespaces & Autoloading
Einführung in NamespacesDas use-SchlüsselwortPSR-4-Autoloading-StandardComposer-AutoloaderRückblick – Organisiertes Projekt5Schnittstellen & Verträge
Einführung in SchnittstellenSchnittstellen implementierenMehrere Schnittstellen implementierenSchnittstelle vs. abstrakte KlasseTyp-Hinweise mit SchnittstellenRückblick – Formrechner8Magische Methoden
Einführung in magische Methoden__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Objektklonen__serialize & __unserializeRückblick – Benutzerdefinierte Sammlung11Typsystem & Fehlerbehandlung
TypdeklarationenNullable-TypenUnion- & Intersection-TypenException-KlassenBenutzerdefinierte Exception-HierarchieTry, Catch, FinallyRückblick – Formularvalidator14Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen3Klassen-Eigenschaften
Instanz- vs. statische EigenschaftenKonstanten in KlassenStatische Methoden & EigenschaftenPrivate & geschützte EigenschaftenSchreibgeschützte Eigenschaften (PHP 8.1)Zusammenfassung – Bankkonto-Manager6Polymorphie
Methodenüberschreibung erneut betrachtetPolymorphie über SchnittstellenTyp-Hinweise & Union-TypenSpätes statisches BindenRückblick – Zahlungsabwickler9Traits
Einführung in TraitsMehrere Traits verwendenAuflösung von Trait-KonfliktenAbstrakte Methoden in TraitsTraits vs. Vererbung12Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster15Abschließende Herausforderungen
E-Learning-PlattformBankingsystemEntwicklung einer SpielfigurFahrzeugvermietungÜbe selbstständig: Online-PHP-Compiler