PHP
Tutorial PHP: Patrones de acceso a datos
PHP: Aprende a usar el Patron Repository o DAO. Desacopla la lógica de acceso a datos y mejora mantenibilidad, promoviendo una arquitectura limpia de persistencia con bases de datos.
Aprende PHP GRATIS y certifícateRepository Pattern y DAO (Data Access Object)
El Patrón Repository y DAO (Data Access Object) son técnicas que permiten desacoplar la lógica de acceso a datos de la lógica de negocio en una aplicación PHP. Este enfoque facilita el mantenimiento, mejora la escalabilidad y promueve una arquitectura más limpia.
El uso de un DAO implica la creación de clases específicas para interactuar con la base de datos u otros medios de persistencia. Estas clases encapsulan las operaciones CRUD (Crear, Leer, Actualizar, Eliminar), proporcionando una interfaz consistente para la capa de negocio.
Por ejemplo, supongamos que tenemos una entidad Usuario en nuestra aplicación. Podemos crear una clase UsuarioDAO
que maneje todas las operaciones de base de datos relacionadas con los usuarios:
<?php
class UsuarioDAO
{
private PDO $conexion;
public function __construct(PDO $conexion)
{
$this->conexion = $conexion;
}
public function obtenerPorId(int $id): ?Usuario
{
$sql = 'SELECT * FROM usuarios WHERE id = :id';
$stmt = $this->conexion->prepare($sql);
$stmt->execute(['id' => $id]);
$datos = $stmt->fetch(PDO::FETCH_ASSOC);
return $datos ? new Usuario($datos['id'], $datos['nombre'], $datos['email']) : null;
}
public function guardar(Usuario $usuario): void
{
if ($usuario->getId()) {
$sql = 'UPDATE usuarios SET nombre = :nombre, email = :email WHERE id = :id';
$params = [
'nombre' => $usuario->getNombre(),
'email' => $usuario->getEmail(),
'id' => $usuario->getId()
];
} else {
$sql = 'INSERT INTO usuarios (nombre, email) VALUES (:nombre, :email)';
$params = [
'nombre' => $usuario->getNombre(),
'email' => $usuario->getEmail()
];
}
$stmt = $this->conexion->prepare($sql);
$stmt->execute($params);
}
// Otros métodos como eliminar, obtenerTodos, etc.
}
En este ejemplo, la clase UsuarioDAO
maneja directamente las consultas a la base de datos utilizando PDO y prepara las declaraciones para evitar inyección SQL. La lógica de negocio no necesita conocer detalles sobre cómo se accede a los datos.
Por otro lado, el Patrón Repository va un paso más allá al abstraer completamente la fuente de datos. La capa de negocio interactúa con interfaces, lo que permite cambiar la implementación de acceso a datos sin modificar la lógica de negocio. Por ejemplo:
<?php
interface UsuarioRepository
{
public function obtenerPorId(int $id): ?Usuario;
public function guardar(Usuario $usuario): void;
// Otros métodos necesarios
}
class MySQLUsuarioRepository implements UsuarioRepository
{
private PDO $conexion;
public function __construct(PDO $conexion)
{
$this->conexion = $conexion;
}
public function obtenerPorId(int $id): ?Usuario
{
// Implementación similar al ejemplo anterior
}
public function guardar(Usuario $usuario): void
{
// Implementación similar al ejemplo anterior
}
// Implementación de otros métodos
}
La interfaz UsuarioRepository
define los métodos que deben implementarse, mientras que MySQLUsuarioRepository
proporciona la implementación específica para MySQL. Si en el futuro se necesita cambiar la fuente de datos a otro sistema (por ejemplo, una API externa), solo es necesario crear una nueva clase que implemente la misma interfaz.
Esta separación de capas promueve una arquitectura más modular y facilita las pruebas unitarias, ya que es posible mockear las implementaciones durante los test. Además, mejora la mantenibilidad al aislar los cambios en una capa específica.
Para utilizar el repositorio en la lógica de negocio:
<?php
class UsuarioService
{
private UsuarioRepository $usuarioRepository;
public function __construct(UsuarioRepository $usuarioRepository)
{
$this->usuarioRepository = $usuarioRepository;
}
public function registrarUsuario(string $nombre, string $email): void
{
$usuario = new Usuario(null, $nombre, $email);
$this->usuarioRepository->guardar($usuario);
// Otras operaciones de negocio
}
// Otros métodos de servicio
}
En este caso, la clase UsuarioService
depende de la interfaz UsuarioRepository
, no de una implementación concreta. Esto permite inyectar diferentes repositorios según las necesidades, facilitando la inyección de dependencias y el uso de principios SOLID.
Para iniciar el servidor web integrado de PHP y probar la aplicación, se puede ejecutar el siguiente comando en el terminal:
php -S localhost:8000
De esta manera, es posible desarrollar y probar las clases sin necesidad de un stack WAMP. Los scripts PHP se ejecutarán en este servidor local.
Separación de capas: ventaja de usar clases específicas para la persistencia
En el desarrollo de aplicaciones PHP, la separación de capas es una práctica esencial que consiste en dividir la aplicación en componentes lógicos independientes. Esta división permite que cada capa se encargue de una responsabilidad específica, mejorando así la mantenibilidad y la escalabilidad del código.
Una de las capas más importantes es la capa de persistencia, responsable del acceso y manipulación de los datos almacenados, generalmente en una base de datos. Utilizar clases específicas para la persistencia proporciona ventajas significativas en el desarrollo de aplicaciones robustas y sostenibles.
Al separar la lógica de acceso a datos en clases especializadas, se consigue una mayor modularidad que facilita el mantenimiento y la evolución del código. Esto permite que los cambios en la base de datos o en la forma de acceder a los datos no afecten a otras capas, como la de presentación o la de negocio.
Por ejemplo, supongamos que tenemos una aplicación que gestiona productos. Podemos crear una clase Producto
que representa la entidad, y una clase ProductoPersistencia
que maneja las operaciones de base de datos relacionadas:
<?php
class Producto
{
private int $id;
private string $nombre;
private float $precio;
public function __construct(int $id, string $nombre, float $precio)
{
$this->id = $id;
$this->nombre = $nombre;
$this->precio = $precio;
}
// Métodos getters y setters
}
class ProductoPersistencia
{
private PDO $conexion;
public function __construct(PDO $conexion)
{
$this->conexion = $conexion;
}
public function guardar(Producto $producto): void
{
$sql = 'INSERT INTO productos (nombre, precio) VALUES (:nombre, :precio)';
$stmt = $this->conexion->prepare($sql);
$stmt->execute([
'nombre' => $producto->getNombre(),
'precio' => $producto->getPrecio()
]);
}
public function obtenerPorId(int $id): ?Producto
{
$sql = 'SELECT * FROM productos WHERE id = :id';
$stmt = $this->conexion->prepare($sql);
$stmt->execute(['id' => $id]);
$datos = $stmt->fetch(PDO::FETCH_ASSOC);
if ($datos) {
return new Producto($datos['id'], $datos['nombre'], $datos['precio']);
}
return null;
}
// Otros métodos como actualizar, eliminar, etc.
}
Con esta estructura, la capa de negocio puede interactuar con los productos sin preocuparse por cómo se almacenan. Si en el futuro se decide cambiar la base de datos de MySQL a PostgreSQL, solo será necesario modificar la clase de persistencia.
La separación de capas también facilita la prueba unitaria del código. Al aislar la lógica de acceso a datos, es posible crear mocks o simulaciones de las clases de persistencia durante las pruebas, evitando dependencias externas y centrándose en la funcionalidad específica que se está evaluando.
Además, esta separación promueve el principio de responsabilidad única, ya que cada clase tiene una función clara y definida. Esto reduce la complejidad y mejora la legibilidad del código, facilitando la colaboración entre desarrolladores en equipos grandes.
En términos de escalabilidad, una arquitectura en capas permite adicionar nuevas funcionalidades o modificar las existentes con menor riesgo de introducir errores. Por ejemplo, si se requiere agregar validaciones adicionales al guardar un producto, se puede hacer en la capa de negocio sin alterar la capa de persistencia.
Para ejecutar y probar esta estructura, se puede utilizar el servidor web integrado de PHP con el siguiente comando:
php -S localhost:8000
Esto permite desarrollar la aplicación sin necesidad de instalar un servidor web adicional, manteniendo el entorno simple y enfocado en el código PHP.
Uso de Query Builders (introducción a ORMs sin entrar en Laravel)
El uso de Query Builders en PHP es una práctica que facilita la construcción de consultas SQL de manera programática y segura. Un Query Builder proporciona una interfaz de cadena de métodos que permite crear consultas complejas sin escribir SQL crudo, reduciendo la posibilidad de errores y vulnerabilidades como la inyección SQL.
Un Query Builder abstrae las consultas SQL mediante métodos que representan distintas partes de una sentencia. Por ejemplo, en lugar de concatenar cadenas para construir una consulta, se utilizan métodos como select()
, from()
, where()
, etc. Esto mejora la legibilidad y el mantenimiento del código.
Existen varias librerías de Query Builders en PHP, siendo una de las más populares la proporcionada por Doctrine DBAL. A continuación, se muestra cómo utilizar el Query Builder de Doctrine para realizar operaciones en una base de datos.
Primero, es necesario instalar Doctrine DBAL mediante Composer:
composer require doctrine/dbal
Una vez instalada la librería, se puede crear una conexión a la base de datos:
<?php
use Doctrine\DBAL\DriverManager;
$conexionParams = [
'dbname' => 'mi_base_de_datos',
'user' => 'mi_usuario',
'password' => 'mi_contraseña',
'host' => 'localhost',
'driver' => 'pdo_mysql',
];
$conexion = DriverManager::getConnection($conexionParams);
Con la conexión establecida, es posible utilizar el QueryBuilder para construir y ejecutar consultas. Por ejemplo, para realizar una selección de datos:
<?php
use Doctrine\DBAL\Query\QueryBuilder;
$queryBuilder = new QueryBuilder($conexion);
$resultado = $queryBuilder
->select('u.id', 'u.nombre', 'u.email')
->from('usuarios', 'u')
->where('u.activo = :activo')
->setParameter(':activo', 1)
->executeQuery()
->fetchAllAssociative();
foreach ($resultado as $usuario) {
echo $usuario['nombre'] . "\n";
}
En este ejemplo, se construye una consulta SELECT que obtiene usuarios activos de la tabla usuarios
. El método setParameter()
se utiliza para asignar valores a los parámetros, evitando inyecciones SQL y mejorando la seguridad.
Para realizar una inserción de datos, el Query Builder permite construir la consulta de manera sencilla:
<?php
$queryBuilder = new QueryBuilder($conexion);
$queryBuilder
->insert('usuarios')
->values([
'nombre' => ':nombre',
'email' => ':email',
'activo' => ':activo',
])
->setParameters([
':nombre' => 'Juan Pérez',
':email' => 'juan.perez@example.com',
':activo' => 1,
])
->executeStatement();
La construcción de consultas de actualización y eliminación sigue una sintaxis similar:
<?php
// Actualizar un usuario
$queryBuilder = new QueryBuilder($conexion);
$queryBuilder
->update('usuarios')
->set('email', ':email')
->where('id = :id')
->setParameters([
':email' => 'nuevo.correo@example.com',
':id' => 1,
])
->executeStatement();
// Eliminar un usuario
$queryBuilder = new QueryBuilder($conexion);
$queryBuilder
->delete('usuarios')
->where('id = :id')
->setParameter(':id', 1)
->executeStatement();
El uso de Query Builders ofrece varias ventajas:
- Seguridad: Al utilizar parámetros enlazados, se previene la inyección SQL de forma efectiva.
- Flexibilidad: Permite construir consultas dinámicas basadas en condiciones variables.
- Mantenibilidad: Mejora la legibilidad y facilita futuras modificaciones en las consultas.
Aunque un Query Builder no es un ORM (Object-Relational Mapping) completo, constituye un paso intermedio hacia la abstracción de bases de datos. Mientras que un ORM mapea directamente las tablas de la base de datos a objetos de PHP, un Query Builder se centra en la construcción de consultas de manera programática sin mapear entidades.
Es importante destacar que el uso de un Query Builder no implica una dependencia de un framework específico como Laravel. De hecho, es posible utilizar librerías independientes que se integren fácilmente en cualquier proyecto PHP.
Para ejecutar estos ejemplos, se puede utilizar el servidor web integrado de PHP con el siguiente comando:
php -S localhost:8000
Esto permite probar el código sin necesidad de configurar un entorno complejo. Los resultados se pueden visualizar en la línea de comandos o en el navegador, dependiendo de cómo se estructure la aplicación.
En resumen, incorporar Query Builders en el desarrollo de aplicaciones PHP mejora la eficiencia y seguridad en el manejo de bases de datos, proporcionando una base sólida para futuras evoluciones hacia ORM más complejos si es necesario.
Todas las lecciones de PHP
Accede a todas las lecciones de PHP y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Php
Introducción Y Entorno
Instalación Y Primer Programa De Php
Introducción Y Entorno
Tipos De Datos, Variables Y Constantes
Sintaxis
Operadores Y Expresiones
Sintaxis
Estructuras De Control
Sintaxis
Funciones Y Llamada De Funciones
Sintaxis
Cadenas De Texto Y Manipulación
Sintaxis
Manejo De Números
Sintaxis
Manejo De Fechas Y Tiempo
Sintaxis
Manejo De Arrays
Sintaxis
Introducción A La Poo En Php
Programación Orientada A Objetos
Clases Y Objetos
Programación Orientada A Objetos
Constructores Y Destructores
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Interfaces
Programación Orientada A Objetos
Traits
Programación Orientada A Objetos
Namespaces
Programación Orientada A Objetos
Autoloading De Clases
Programación Orientada A Objetos
Manejo De Errores Y Excepciones
Programación Orientada A Objetos
Manejo De Archivos
Programación Orientada A Objetos
Patrones De Diseño
Programación Orientada A Objetos
Introducción A Los Formularios En Php
Formularios
Procesamiento De Datos De Formularios
Formularios
Manejo De Archivos En Formularios
Formularios
Redirecciones Y Retroalimentación Al Usuario
Formularios
Formularios Dinámicos Y Separación De Lógica
Formularios
Introducción A La Persistencia En Php
Persistencia
Conexión A Bases De Datos
Persistencia
Consultas Y Operaciones Crud
Persistencia
Gestión De Transacciones
Persistencia
Manejo De Errores Y Excepciones En Base De Datos
Persistencia
Patrones De Acceso A Datos
Persistencia
Concepto De Sesiones En Php
Sesiones Y Cookies
Configuración De Sesiones
Sesiones Y Cookies
Cookies
Sesiones Y Cookies
Manejo Avanzado De Sesiones Y Cookies
Sesiones Y Cookies
Principales Vulnerabilidades En Php
Seguridad
Seguridad En Formularios Y Entrada De Datos
Seguridad
Protección Frente A Inyección Sql
Seguridad
Gestión De Contraseñas Y Cifrado
Seguridad
Seguridad En Sesiones Y Cookies
Seguridad
Configuraciones De Php Para Seguridad
Seguridad
Introducción Al Testing En Php
Testing
Phpunit
Testing
Cobertura De Código En Testing
Testing
Test Doubles (Mocks, Stubs, Fakes, Spies)
Testing
Pruebas De Integración Y Funcionales
Testing
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender el Patrón Repository y DAO en PHP.
- Implementar operaciones CRUD con clases DAO.
- Diferenciar entre acceso a datos y lógica de negocio.
- Aplicar el principio de separación de capas.
- Integrar repositorios en la lógica de negocio.