Patron Repository
Fait partie de la section Programmation Orientée Objet du Journey PHP de Coddy — leçon 81 sur 91.
Le Repository Pattern est un patron de conception qui sert de médiateur entre la logique métier de votre application et le stockage des données. Il fournit une interface de type collection pour accéder aux objets du domaine, masquant les détails sur la manière dont les données sont réellement stockées ou récupérées.
Considérez un repository comme une collection spécialisée qui sait comment persister et récupérer des objets. Le code de votre application demande au repository un utilisateur par son ID ou enregistre un nouveau produit, sans savoir si les données résident dans une base de données, un fichier ou une API. Cette séparation rend votre code plus testable et flexible.
<?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
) {}
}
L'implémentation du dépôt (repository) gère le mécanisme de stockage réel :
<?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());
Résultat :
Alice
2La beauté de ce patron de conception est que vous pouvez interchanger les implémentations sans modifier votre logique métier. Besoin de passer d'un stockage en mémoire à une base de données ? Créez une nouvelle classe repository implémentant la même interface. Le code de votre application reste inchangé car il dépend de l'interface, et non de l'implémentation concrète.
Défi
FacileConstruisons un système d'inventaire de produits en utilisant le Repository Pattern. Vous allez créer une séparation nette entre vos objets de domaine et la manière dont ils sont stockés, permettant à votre code d'application de manipuler les produits sans connaître les détails du stockage.
Vous organiserez votre code sur quatre fichiers :
Product.php— Créez une classeProductreprésentant les articles de votre inventaire. Chaque produit possède unid(int), unname(string) et unprice(float). Utilisez la promotion de constructeur avec des propriétés publiques pour un accès facile.ProductRepositoryInterface.php— Définissez uneProductRepositoryInterfacequi établit le contrat pour toute implémentation de stockage de produits. Votre interface doit déclarer ces méthodes :find(int $id): ?Product— Récupère un produit par son ID, ou null s'il n'est pas trouvéfindAll(): array— Récupère tous les produits sous forme de tableausave(Product $product): void— Stocke ou met à jour un produitdelete(int $id): void— Supprime un produit par son ID
InMemoryProductRepository.php— Incluez à la fois la classe Product et l'interface. Créez une classeInMemoryProductRepositoryqui implémente l'interface en utilisant un tableau privé pour stocker les produits. Les produits doivent être indexés par leur ID pour une recherche efficace. La méthodefindAll()doit retourner uniquement les objets produits (utilisezarray_values()pour réinitialiser les clés du tableau).main.php— Incluez le fichier du dépôt. Vous recevrez une entrée : une chaîne JSON contenant un tableau de produits à ajouter, suivie d'un ID à rechercher.Le format JSON sera :
[{"id": 1, "name": "Laptop", "price": 999.99}, {"id": 2, "name": "Mouse", "price": 29.99}]Créez une instance de
InMemoryProductRepository, sauvegardez tous les produits provenant de l'entrée JSON, puis utilisez la deuxième entrée (l'ID) pour trouver un produit spécifique. Affichez les résultats dans ce format :Total products: [count] Found: [name] - $[price]Formatez le prix avec deux décimales. Si le produit n'est pas trouvé, affichez
Found: Not foundà la place.
Le Repository Pattern prend tout son sens lorsque vous devez changer d'implémentation de stockage. Votre code principal ne dépend que de l'interface — passer d'un stockage en mémoire à une base de données signifierait créer une nouvelle classe de dépôt sans modifier la logique de l'application.
Aide-mémoire
Le Repository Pattern (ou patron de conception Répertoire) sert de médiateur entre la logique métier et le stockage des données, fournissant une interface de type collection pour accéder aux objets du domaine tout en masquant les détails d'implémentation du stockage.
Définissez une interface de répertoire qui établit le contrat pour l'accès aux données :
<?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;
}Créez une classe de domaine en utilisant la promotion de constructeur :
<?php
class User {
public function __construct(
public int $id,
public string $name,
public string $email
) {}
}Implémentez l'interface de répertoire avec un mécanisme de stockage concret :
<?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]);
}
}Utilisez le répertoire via son interface :
$repo = new InMemoryUserRepository();
$repo->save(new User(1, "Alice", "alice@example.com"));
$user = $repo->find(1);
$allUsers = $repo->findAll();Ce patron permet d'échanger les implémentations sans modifier la logique métier — votre code dépend de l'interface, et non de l'implémentation concrète.
Essayez vous-même
<?php
require_once 'InMemoryProductRepository.php';
// Lire l'entrée
$jsonInput = trim(fgets(STDIN));
$searchId = intval(trim(fgets(STDIN)));
// Analyser l'entrée JSON dans un tableau
$productsData = (array)json_decode($jsonInput, true);
// TODO: Créer une instance de InMemoryProductRepository
// TODO: Parcourir $productsData et sauvegarder chaque produit
// Chaque élément possède les clés 'id', 'name' et 'price'
// TODO: Récupérer tous les produits et afficher le nombre total
// Format: "Total products: [count]"
// TODO: Trouver le produit avec $searchId
// Si trouvé, afficher: "Found: [name] - $[price]" (prix formaté à 2 décimales)
// Si non trouvé, afficher: "Found: Not found"
?>Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Les bases de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe mot-clé $thisMéthodesPropriétésConstructeur (__construct)Destructeur (__destruct)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLe mot-clé parent::Redéfinition de méthodeLe mot-clé finalClasses abstraitesRécapitulatif - Hiérarchie des employés7Encapsulation
Public, Protégé, PrivéModificateurs d'accès en détailGetters et SettersMasquage d'informationPromotion de constructeur (8.0)Récapitulatif - Système de gestion d'étudiants10Concepts avancés de la POO
Composition vs HéritageInjection de dépendancesClasses anonymesÉnumérations (PHP 8.1)Fibers (PHP 8.1)Clonage d'objets : analyse approfondieGénérateurs et Itérateurs13Patrons de conception Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode ModèlePatron ÉtatPatron CompositePatron Repository2Espaces de noms et Autoloading
Introduction aux espaces de nomsLe mot-clé useStandard d'Autoloading PSR-4L'Autoloader ComposerRécapitulatif - Projet organisé5Interfaces et Contrats
Introduction aux InterfacesImplémentation d'InterfacesImplémentation d'Interfaces MultiplesInterface vs Classe AbstraiteType Hinting avec les InterfacesRécapitulatif - Calculateur de Formes8Méthodes magiques
Introduction aux méthodes magiques__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Clonage d'objets__serialize & __unserializeRécapitulatif - Collection personnalisée3Propriétés de classe
Propriétés d'instance vs Propriétés statiquesConstantes dans les classesMéthodes et propriétés statiquesPropriétés privées et protégéesPropriétés readonly (PHP 8.1)Récapitulatif - Gestionnaire de compte bancaire6Polymorphisme
Retour sur la redéfinition de méthodePolymorphisme via les interfacesType Hinting et Types d'unionLiaison statique tardiveRécapitulatif - Processeur de paiement9Traits
Introduction aux TraitsUtilisation de plusieurs TraitsRésolution des conflits de TraitsMéthodes abstraites dans les TraitsTraits vs Héritage12Patrons de conception Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron ObservateurPatron Stratégie