Patrón Repository
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy. Lección 81 de 91.
El Repository Pattern es un patrón de diseño que actúa como intermediario entre la lógica de negocio de tu aplicación y el almacenamiento de datos. Proporciona una interfaz similar a una colección para acceder a objetos del dominio, ocultando los detalles de cómo se almacenan o recuperan realmente los datos.
Piensa en un repositorio como una colección especializada que sabe cómo persistir y recuperar objetos. El código de tu aplicación le pide al repositorio un usuario por ID o guarda un producto nuevo, sin saber si los datos se encuentran en una base de datos, un archivo o una API. Esta separación hace que tu código sea más fácil de probar y 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
) {}
}
La implementación del repositorio gestiona el mecanismo de almacenamiento real:
<?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());
Salida:
Alice
2La belleza de este patrón es que puedes intercambiar implementaciones sin cambiar la lógica de negocio. ¿Necesitas cambiar del almacenamiento en memoria a una base de datos? Crea una nueva clase de repositorio que implemente la misma interfaz. El código de tu aplicación permanece intacto porque depende de la interfaz, no de la implementación concreta.
Desafío
FácilConstruyamos un sistema de inventario de productos usando el patrón Repository. Crearás una separación clara entre tus objetos de dominio y la forma en que se almacenan, lo que permitirá que el código de tu aplicación trabaje con productos sin conocer los detalles del almacenamiento.
Organizarás tu código en cuatro archivos:
Product.php: Crea una claseProductque represente los artículos de tu inventario. Cada producto tiene unid(int), unname(string) y unprice(float). Usa la promoción del constructor con propiedades públicas para facilitar el acceso.ProductRepositoryInterface.php: Define unProductRepositoryInterfaceque establezca el contrato para cualquier implementación de almacenamiento de productos. Tu interfaz debe declarar estos métodos:find(int $id): ?Product: Recupera un producto por ID o devuelve null si no se encuentrafindAll(): array: Obtiene todos los productos como un arraysave(Product $product): void: Almacena o actualiza un productodelete(int $id): void: Elimina un producto por ID
InMemoryProductRepository.php: Incluye tanto la clase Product como la interfaz. Crea una claseInMemoryProductRepositoryque implemente la interfaz usando un array privado para almacenar los productos. Los productos deben estar indexados por su ID para realizar búsquedas de manera eficiente. El métodofindAll()debe devolver únicamente los objetos de producto (usaarray_values()para restablecer las claves del array).main.php: Incluye el archivo del repositorio. Recibirás dos entradas: primero, una cadena JSON de productos que se deben añadir; después, en una línea separada, el ID entero que se debe buscar.El formato JSON será:
[{"id": 1, "name": "Laptop", "price": 999.99}, {"id": 2, "name": "Mouse", "price": 29.99}]Crea un
InMemoryProductRepository, guarda todos los productos de la entrada JSON y después usa la segunda entrada (el ID) para buscar un producto específico. Imprime los resultados con este formato:Total products: [count] Found: [name] - $[price]Da formato al precio con dos decimales. Si no se encuentra el producto, imprime
Found: Not founden su lugar.
El patrón Repository resulta especialmente útil cuando necesitas cambiar las implementaciones de almacenamiento. Tu código principal depende únicamente de la interfaz: cambiar del almacenamiento en memoria a una base de datos implicaría crear una nueva clase de repositorio sin modificar ninguna lógica de la aplicación.
Pruébalo tú mismo
<?php
require_once 'InMemoryProductRepository.php';
// Leer entrada
$jsonInput = trim(fgets(STDIN));
$searchId = intval(trim(fgets(STDIN)));
// Analizar la entrada JSON en un array
$productsData = (array)json_decode($jsonInput, true);
// TODO: Crear una instancia de InMemoryProductRepository
// TODO: Recorrer $productsData y guardar cada producto
// Cada elemento tiene las claves 'id', 'name' y 'price'
// TODO: Obtener todos los productos e imprimir el conteo total
// Format: "Total products: [count]"
// TODO: Encontrar el producto con $searchId
// Si se encuentra, imprimir: "Found: [name] - $[price]" (precio formateado a 2 decimales)
// If not found, print: "Found: Not found"
?>Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de la POO
Archivos externosIntroducción a la POOClases frente a objetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Repaso: calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasRepaso - Jerarquía de empleados7Encapsulación
Público, protegido, privadoModificadores de acceso en profundidadGetters y settersOcultación de la informaciónPromoción del constructor (8.0)Repaso: sistema de registros de estudiantes10Conceptos avanzados de OOP
Composición vs. herenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibras (PHP 8.1)Análisis profundo de la clonación de objetosGeneradores e iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Namespaces y carga automática
Introducción a los NamespacesLa palabra clave useEstándar de carga automática PSR-4Autoloader de ComposerRepaso: proyecto organizado5Interfaces y contratos
Introducción a las interfacesImplementación de interfacesImplementación de múltiples interfacesInterfaces vs. clase abstractaTipado con interfacesRepaso - Calculadora de formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeRepaso: colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Repaso: gestor de cuentas bancarias6Polimorfismo
Repaso de la sobrescritura de métodosPolimorfismo mediante interfacesAnotaciones de tipo y tipos uniónEnlace estático tardíoRepaso: procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos entre TraitsMétodos abstractos en TraitsTraits frente a la herencia12Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón StrategyPractica por tu cuenta: Compilador de PHP online