Wzorzec repozytorium
Część sekcji Programowanie obiektowe ścieżki PHP w Coddy. Lekcja 81 z 91.
Wzorzec repozytorium to wzorzec projektowy, który pośredniczy między logiką biznesową Twojej aplikacji a przechowywaniem danych. Zapewnia interfejs podobny do kolekcji, umożliwiający dostęp do obiektów domenowych, ukrywając szczegóły faktycznego przechowywania i pobierania danych.
Repository możesz sobie wyobrazić jako wyspecjalizowaną kolekcję, która wie, jak przechowywać i pobierać obiekty. Kod aplikacji prosi repozytorium o użytkownika na podstawie ID lub zapisuje nowy produkt, nie wiedząc, czy dane znajdują się w bazie danych, pliku czy API. Taki podział sprawia, że kod łatwiej testować i jest bardziej elastyczny.
<?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
) {}
}
Implementacja repozytorium obsługuje mechanizm przechowywania danych:
<?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());
Wynik:
Alice
2Piękno tego wzorca polega na tym, że możesz wymieniać implementacje bez zmieniania logiki biznesowej. Chcesz przejść z przechowywania danych w pamięci na bazę danych? Utwórz nową klasę repozytorium implementującą ten sam interfejs. Kod aplikacji pozostaje nietknięty, ponieważ zależy od interfejsu, a nie od konkretnej implementacji.
Wyzwanie
ŁatwyZbudujmy system ewidencji produktów z wykorzystaniem wzorca Repository. Utworzysz wyraźny podział między obiektami domenowymi a sposobem ich przechowywania, dzięki czemu kod aplikacji będzie mógł pracować z produktami bez znajomości szczegółów przechowywania.
Podzielisz kod na cztery pliki:
Product.php: Utwórz klasęProductreprezentującą produkty w Twoim magazynie. Każdy produkt maid(int),name(string) iprice(float). Użyj promocji konstruktora z publicznymi właściwościami, aby ułatwić dostęp.ProductRepositoryInterface.php: ZdefiniujProductRepositoryInterface, który określa kontrakt dla każdej implementacji przechowywania produktów. Twój interfejs powinien deklarować następujące metody:find(int $id): ?Product: Pobierz produkt według ID albo null, jeśli nie został znalezionyfindAll(): array: Pobierz wszystkie produkty jako tablicęsave(Product $product): void: Zapisz lub zaktualizuj produktdelete(int $id): void: Usuń produkt według ID
InMemoryProductRepository.php: Dołącz zarówno klasę Product, jak i interfejs. Utwórz klasęInMemoryProductRepository, która implementuje interfejs, używając prywatnej tablicy do przechowywania produktów. Produkty powinny być indeksowane według ich ID, aby umożliwić szybkie wyszukiwanie. MetodafindAll()powinna zwracać wyłącznie obiekty produktów (użyjarray_values(), aby zresetować klucze tablicy).main.php: Dołącz plik repozytorium. Otrzymasz dwa wejścia: najpierw ciąg JSON z produktami do dodania, a następnie, w osobnym wierszu, całkowitą liczbę ID produktu do wyszukania.Format JSON będzie następujący:
[{"id": 1, "name": "Laptop", "price": 999.99}, {"id": 2, "name": "Mouse", "price": 29.99}]Utwórz
InMemoryProductRepository, zapisz wszystkie produkty z wejścia JSON, a następnie użyj drugiego wejścia (ID), aby znaleźć konkretny produkt. Wypisz wyniki w tym formacie:Total products: [count] Found: [name] - $[price]Sformatuj cenę do dwóch miejsc po przecinku. Jeśli produkt nie zostanie znaleziony, wypisz
Found: Not found.
Wzorzec Repository sprawdza się, gdy trzeba zamienić jedną implementację przechowywania danych na inną. Główny kod zależy wyłącznie od interfejsu: przejście z przechowywania w pamięci na bazę danych wymagałoby utworzenia nowej klasy repozytorium bez zmieniania jakiejkolwiek logiki aplikacji.
Spróbuj swoich sił
<?php
require_once 'InMemoryProductRepository.php';
// Odczytaj dane wejściowe
$jsonInput = trim(fgets(STDIN));
$searchId = intval(trim(fgets(STDIN)));
// Przekształć dane wejściowe JSON w tablicę
$productsData = (array)json_decode($jsonInput, true);
// TODO: Utwórz instancję InMemoryProductRepository
// TODO: Przejdź przez $productsData i zapisz każdy produkt
// Każdy element ma klucze 'id', 'name' i 'price'
// TODO: Pobierz wszystkie produkty i wypisz ich łączną liczbę
// Format: "Total products: [count]"
// TODO: Znajdź produkt o identyfikatorze $searchId
// Jeśli zostanie znaleziony, wypisz: "Found: [name] - $[price]" (cenę sformatuj do 2 miejsc po przecinku)
// Jeśli nie zostanie znaleziony, wypisz: "Found: Not found"
?>Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe $thisMetodyWłaściwościKonstruktor (__construct)Destruktor (__destruct)Powtórka — prosty kalkulator4Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe parent::Przesłanianie metodSłowo kluczowe finalKlasy abstrakcyjnePowtórzenie – hierarchia pracowników7Enkapsulacja
Public, protected, privateModyfikatory dostępu w szczegółachGettery i setteryUkrywanie informacjiPromocja parametrów konstruktora (8.0)Podsumowanie — system ewidencji studentów10Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieWstrzykiwanie zależnościKlasy anonimoweEnumy (PHP 8.1)Włókna (PHP 8.1)Dogłębne omówienie klonowania obiektówGeneratory i iteratory13Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytuWzorzec repozytorium2Przestrzenie nazw i autoloading
Wprowadzenie do przestrzeni nazwSłowo kluczowe useStandard autoloadingu PSR-4Autoloader ComposeraPodsumowanie – uporządkowany projekt5Interfejsy i kontrakty
Wprowadzenie do interfejsówImplementowanie interfejsówImplementowanie wielu interfejsówInterfejs a klasa abstrakcyjnaPodpowiadanie typów za pomocą interfejsówPodsumowanie — kalkulator kształtów8Metody magiczne
Wprowadzenie do metod magicznych__toString i __debugInfo__get, __set, __isset, __unset__call i __callStatic__clone i klonowanie obiektów__serialize i __unserializePodsumowanie — własna kolekcja11System typów i obsługa błędów
Deklaracje typówTypy nullableTypy sumy i przecięciaKlasy wyjątkówNiestandardowa hierarchia wyjątkówTry, Catch, FinallyPodsumowanie — walidator formularzy14Projekt: Zarządzanie biblioteką
Przegląd projektuKlasy książki i użytkownika3Właściwości klas
Właściwości instancji a właściwości statyczneStałe w klasachMetody i właściwości statyczneWłaściwości prywatne i chronioneWłaściwości tylko do odczytu (PHP 8.1)Podsumowanie — menedżer kont bankowych6Polimorfizm
Ponowne omówienie nadpisywania metodPolimorfizm za pomocą interfejsówPodpowiedzi typów i typy sumyPóźne wiązanie statycznePodsumowanie — procesor płatności9Cechy
Wprowadzenie do cechKorzystanie z wielu cechRozwiązywanie konfliktów cechMetody abstrakcyjne w cechachCechy a dziedziczenie12Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec StrategyPoćwicz samodzielnie: Kompilator PHP online