Menu
Coddy logo textTech

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 Patrón Repository es un patrón de diseño que media entre la lógica de negocio de su aplicación y el almacenamiento de datos. Proporciona una interfaz similar a una colección para acceder a los objetos de dominio, ocultando los detalles de cómo se almacenan o recuperan realmente los datos.

Piense en un repositorio como una colección especializada que sabe cómo persistir y recuperar objetos. El código de su aplicación le pide al repositorio un usuario por ID o guarda un nuevo producto, sin saber si los datos residen en una base de datos, un archivo o una API. Esta separación hace que su código sea más fácil de probar y más 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 maneja 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
2

La belleza de este patrón es que puedes intercambiar implementaciones sin cambiar tu 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.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de inventario de productos utilizando el Patrón Repository. Crearás una separación clara entre tus objetos de dominio y cómo se almacenan, permitiendo 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 clase Product que represente los artículos en tu inventario. Cada producto tiene un id (int), name (string) y price (float). Utiliza la promoción de constructor con propiedades públicas para un acceso fácil.
  • ProductRepositoryInterface.php — Define una ProductRepositoryInterface que establezca el contrato para cualquier implementación de almacenamiento de productos. Tu interfaz debe declarar estos métodos:
    • find(int $id): ?Product — Recuperar un producto por ID, o null si no se encuentra
    • findAll(): array — Obtener todos los productos como un array
    • save(Product $product): void — Almacenar o actualizar un producto
    • delete(int $id): void — Eliminar un producto por ID
  • InMemoryProductRepository.php — Incluye tanto la clase Product como la interfaz. Crea una clase InMemoryProductRepository que implemente la interfaz utilizando un array privado para almacenar los productos. Los productos deben estar indexados por su ID para una búsqueda eficiente. El método findAll() debe devolver solo los objetos de producto (usa array_values() para restablecer las claves del array).
  • main.php — Incluye el archivo del repositorio. Recibirás una entrada: una cadena JSON que contiene un array de productos para agregar, seguida de un ID para 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, luego usa la segunda entrada (el ID) para encontrar un producto específico. Imprime los resultados en este formato:

    Total products: [count]
    Found: [name] - $[price]

    Formatea el precio con dos decimales. Si el producto no se encuentra, imprime Found: Not found en su lugar.

El Patrón Repository destaca cuando necesitas intercambiar implementaciones de almacenamiento. Tu código principal depende solo de la interfaz; cambiar del almacenamiento en memoria a una base de datos significaría crear una nueva clase de repositorio sin cambiar ninguna lógica de la aplicación.

Hoja de referencia

El Patrón de Repositorio actúa como mediador entre la lógica de negocio y el almacenamiento de datos, proporcionando una interfaz similar a una colección para acceder a objetos de dominio mientras oculta los detalles de implementación del almacenamiento.

Define una interfaz de repositorio que establezca el contrato para el acceso a datos:

<?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;
}

Crea una clase de dominio utilizando la promoción de propiedades en el constructor:

<?php
class User {
    public function __construct(
        public int $id,
        public string $name,
        public string $email
    ) {}
}

Implementa la interfaz del repositorio con un mecanismo de almacenamiento concreto:

<?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]);
    }
}

Utiliza el repositorio a través de su interfaz:

$repo = new InMemoryUserRepository();
$repo->save(new User(1, "Alice", "alice@example.com"));
$user = $repo->find(1);
$allUsers = $repo->findAll();

El patrón permite intercambiar implementaciones sin cambiar la lógica de negocio; su código depende de la interfaz, no de la implementación concreta.

Pruébalo tú mismo

<?php

require_once 'InMemoryProductRepository.php';

// Leer la 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 recuento total
// Formato: "Total products: [count]"

// TODO: Buscar el producto con $searchId
// Si se encuentra, imprimir: "Found: [name] - $[price]" (precio formateado a 2 decimales)
// Si no se encuentra, imprimir: "Found: Not found"

?>
quiz iconPonte a prueba

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