13-05-2026

Patrón repository en php: qué es y cómo implementarlo

En este post se muestra un ejemplo del Patrón Repository en PHP.

Estructura de carpetas recomendada

src/
├── Domain/
│└── User.php
├── Infrastructure/
│└── Persistence/
│  ├── UserRepository.php
│  └── UserRepositoryInterface.php
└── index.php

Entidad (Domain)

// src/Domain/User.php
namespace App\Domain;
class User
{
public function __construct(
public readonly ?int $id,
public string $name,
public string $email
) {}
}

Interfaz del Repository

// src/Infrastructure/Persistence/UserRepositoryInterface.php
namespace App\Infrastructure\Persistence;

use App\Domain\User;

interface UserRepositoryInterface
{
public function findById(int $id): ?User;
public function findAll(): array;
public function save(User $user): void;
public function delete(int $id): bool;
}

Implementación con MySQL (PDO)

// src/Infrastructure/Persistence/UserRepository.php
namespace App\Infrastructure\Persistence;

use App\Domain\User;
use PDO;
use PDOException;

class UserRepository implements UserRepositoryInterface
{
private PDO $db;

public function __construct(PDO $db)
{
$this->db = $db;
}

public function findById(int $id): ?User
{
$stmt = $this->db->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $id]);
$data = $stmt->fetch(PDO::FETCH_ASSOC);

if (!$data) {
  return null;
}

return new User(
  (int)$data['id'],
  $data['name'],
  $data['email']
);
}

public function findAll(): array
{
$stmt = $this->db->query("SELECT * FROM users");
$users = [];

while ($data = $stmt->fetch(PDO::FETCH_ASSOC)) {
  $users[] = new User(
  (int)$data['id'],
  $data['name'],
  $data['email']
  );
}

return $users;
}

public function save(User $user): void
{
if ($user->id === null) {
  // Insertar nuevo usuario
  $stmt = $this->db->prepare("INSERT INTO users (name, email) VALUES (:name, :email)");
  $stmt->execute([
  'name' => $user->name,
  'email' => $user->email
  ]);
} else {
  // Actualizar usuario existente
  $stmt = $this->db->prepare("UPDATE users SET name = :name, email = :email WHERE id = :id");
  $stmt->execute([
  'id' => $user->id,
  'name' => $user->name,
  'email' => $user->email
  ]);
}
}

public function delete(int $id): bool
{
$stmt = $this->db->prepare("DELETE FROM users WHERE id = :id");
return $stmt->execute(['id' => $id]);
}
}

Uso (index.php)

// index.php
require_once __DIR__ . '/vendor/autoload.php'; //Si se usaComposer

use App\Domain\User;
use App\Infrastructure\Persistence\UserRepository;
use PDO;

// Configuración de la base de datos
try {
$pdo = new PDO(
'mysql:host=localhost;dbname=mi_app;charset=utf8mb4',
'root',  // usuario
'', // contraseña
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
} catch (PDOException $e) {
die("Error de conexión: " . $e->getMessage());
}

// Crear el Repository
$repository = new UserRepository($pdo);

// === Ejemplos de uso ===

// Crear usuario
$newUser = new User(null, "Carlos Ramírez", "carlos@example.com");
$repository->save($newUser);

echo "Usuario guardado correctamente!
"; // Buscar por ID $user = $repository->findById(1); if ($user) { echo "Usuario encontrado: {$user->name} - {$user->email}
"; } // Listar todos echo "Todos los usuarios:"; foreach ($repository->findAll() as $u) { echo "- {$u->name} ({$u->email})
"; }

Tabla en MySQL

CREATE DATABASE mi_app CHARACTER SET utf8mb4;

USE mi_app;

CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);