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 Ihrer Anwendung und der Datenspeicherung vermittelt. Es bietet eine kollektionsartige Schnittstelle für den Zugriff auf Domänenobjekte und verbirgt die Details darüber, wie Daten tatsächlich gespeichert oder abgerufen werden.
Stellen Sie sich ein Repository als eine spezialisierte Sammlung vor, die weiß, wie Objekte persistiert und abgerufen werden. Ihr Anwendungscode fragt das Repository nach einem Benutzer anhand der ID oder speichert ein neues Produkt, ohne zu wissen, ob die Daten in einer Datenbank, einer Datei oder einer API liegen. Diese Trennung macht Ihren Code testbarer 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
2Die Schönheit dieses Entwurfsmusters liegt darin, dass Sie Implementierungen austauschen können, ohne Ihre Geschäftslogik zu ändern. Müssen Sie von der In-Memory-Speicherung zu einer Datenbank wechseln? Erstellen Sie eine neue Repository-Klasse, die dasselbe Interface implementiert. Ihr Anwendungscode bleibt unberührt, da er vom Interface und nicht von der konkreten Implementierung abhängt.
Aufgabe
EinfachLassen Sie uns ein Produktinventarsystem mit dem Repository-Pattern erstellen. Sie werden eine saubere Trennung zwischen Ihren Domänenobjekten und deren Speicherung schaffen, sodass Ihr Anwendungscode mit Produkten arbeiten kann, ohne die Details der Speicherung zu kennen.
Sie werden Ihren Code über vier Dateien organisieren:
Product.php— Erstellen Sie eineProduct-Klasse, die Artikel in Ihrem Inventar repräsentiert. Jedes Produkt hat eineid(int), einenname(string) und einenprice(float). Verwenden Sie Constructor Promotion mit öffentlichen Eigenschaften für einen einfachen Zugriff.ProductRepositoryInterface.php— Definieren Sie einProductRepositoryInterface, das den Vertrag für jede Produktspeicher-Implementierung festlegt. Ihr Interface sollte diese Methoden deklarieren:find(int $id): ?Product— Ruft ein Produkt anhand der ID ab oder null, falls es nicht gefunden wurdefindAll(): array— Ruft alle Produkte als Array absave(Product $product): void— Speichert oder aktualisiert ein Produktdelete(int $id): void— Entfernt ein Produkt anhand der ID
InMemoryProductRepository.php— Binden Sie sowohl die Product-Klasse als auch das Interface ein. Erstellen Sie eineInMemoryProductRepository-Klasse, die das Interface implementiert und ein privates Array zum Speichern von Produkten verwendet. Produkte sollten für eine effiziente Suche über ihre ID indiziert werden. Die MethodefindAll()sollte nur die Produktobjekte zurückgeben (verwenden Siearray_values(), um die Array-Keys zurückzusetzen).main.php— Binden Sie die Repository-Datei ein. Sie erhalten eine Eingabe: einen JSON-String, der ein Array von hinzuzufügenden Produkten enthält, gefolgt von einer ID, nach der gesucht werden soll.Das JSON-Format wird wie folgt aussehen:
[{"id": 1, "name": "Laptop", "price": 999.99}, {"id": 2, "name": "Mouse", "price": 29.99}]Erstellen Sie ein
InMemoryProductRepository, speichern Sie alle Produkte aus der JSON-Eingabe und verwenden Sie dann die zweite Eingabe (die ID), um ein bestimmtes Produkt zu finden. Geben Sie die Ergebnisse in diesem Format aus:Total products: [count] Found: [name] - $[price]Formatieren Sie den Preis auf zwei Dezimalstellen. Wenn das Produkt nicht gefunden wird, geben Sie stattdessen
Found: Not foundaus.
Das Repository-Pattern glänzt, wenn Sie Speicherimplementierungen austauschen müssen. Ihr Hauptcode hängt nur vom Interface ab – der Wechsel von In-Memory-Speicherung zu einer Datenbank würde bedeuten, eine neue Repository-Klasse zu erstellen, ohne die Anwendungslogik zu ändern.
Spickzettel
Das Repository Pattern vermittelt zwischen Geschäftslogik und Datenspeicherung und bietet eine kollektionsähnliche Schnittstelle für den Zugriff auf Domänenobjekte, während die Details der Speicherimplementierung verborgen bleiben.
Definieren Sie ein Repository-Interface, das den Vertrag für den Datenzugriff festlegt:
<?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;
}Erstellen Sie eine Domänenklasse unter Verwendung von Constructor Promotion:
<?php
class User {
public function __construct(
public int $id,
public string $name,
public string $email
) {}
}Implementieren Sie das Repository-Interface mit einem konkreten 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]);
}
}Verwenden Sie das Repository über sein Interface:
$repo = new InMemoryUserRepository();
$repo->save(new User(1, "Alice", "alice@example.com"));
$user = $repo->find(1);
$allUsers = $repo->findAll();Das Pattern ermöglicht das Austauschen von Implementierungen, ohne die Geschäftslogik zu ändern – Ihr Code hängt vom Interface ab, nicht von der konkreten Implementierung.
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 umwandeln
$productsData = (array)json_decode($jsonInput, true);
// TODO: Eine Instanz von InMemoryProductRepository 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 Gesamtzahl ausgeben
// Format: "Total products: [count]"
// TODO: Das Produkt mit der $searchId finden
// Falls gefunden, ausgeben: "Found: [name] - $[price]" (Preis auf 2 Dezimalstellen formatiert)
// Falls nicht gefunden, ausgeben: "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 die OOPKlassen vs. ObjekteDas $this-SchlüsselwortMethodenEigenschaftenKonstruktor (__construct)Destruktor (__destruct)Rückblick - Einfacher Taschenrechner4Vererbung
Grundlagen der VererbungDas parent:: SchlüsselwortMethoden überschreibenDas final SchlüsselwortAbstrakte KlassenZusammenfassung – Mitarbeiter-Hierarchie7Kapselung
Public, Protected, PrivateZugriffsmodifikatoren im DetailGetter und SetterInformation HidingConstructor Promotion (8.0)Zusammenfassung – Studentenverwaltungssystem10Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungDependency InjectionAnonyme KlassenEnums (PHP 8.1)Fibers (PHP 8.1)Objekt-Klonen im DetailGeneratoren & Iteratoren13Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterZustandsmusterComposite-MusterRepository-Muster2Namensräume & Autoloading
Einführung in NamensräumeDas use-SchlüsselwortPSR-4 Autoloading-StandardComposer AutoloaderZusammenfassung – Organisiertes Projekt5Interfaces & Verträge
Einführung in InterfacesInterfaces implementierenMehrfache Interface-ImplementierungInterface vs. Abstrakte KlasseType Hinting mit InterfacesZusammenfassung – Formen-Rechner8Magische Methoden
Einführung in magische Methoden__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Objekt-Klonen__serialize & __unserializeZusammenfassung - Eigene Collection11Typsystem & Fehlerbehandlung
TypdeklarationenNullable-TypenUnion- & Intersection-TypenException-KlassenEigene Exception-HierarchieTry, Catch, FinallyZusammenfassung - Formular-Validator14Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen3Klasseneigenschaften
Instanz- vs. statische EigenschaftenKonstanten in KlassenStatische Methoden & EigenschaftenPrivate & geschützte EigenschaftenReadonly-Eigenschaften (PHP 8.1)Zusammenfassung – Bankkonto-Manager6Polymorphismus
Method Overriding erneut betrachtetPolymorphismus über InterfacesType Hinting & Union TypesLate Static BindingZusammenfassung – Payment Processor9Traits
Einführung in TraitsVerwendung mehrerer TraitsKonfliktlösung bei TraitsAbstrakte Methoden in TraitsTraits vs. Vererbung12Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster15Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung