PHP

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ícate

Repository 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.

Para seguir leyendo hazte Plus

¿Ya eres Plus? Accede a la app

20 % DE DESCUENTO

Plan mensual

19.00 /mes

15.20 € /mes

Precio normal mensual: 19 €
58 % DE DESCUENTO

Plan anual

10.00 /mes

8.00 € /mes

Ahorras 132 € al año
Precio normal anual: 120 €
Aprende PHP GRATIS online

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

PHP

Introducción Y Entorno

Instalación Y Primer Programa De Php

PHP

Introducción Y Entorno

Tipos De Datos, Variables Y Constantes

PHP

Sintaxis

Operadores Y Expresiones

PHP

Sintaxis

Estructuras De Control

PHP

Sintaxis

Funciones Y Llamada De Funciones

PHP

Sintaxis

Cadenas De Texto Y Manipulación

PHP

Sintaxis

Manejo De Números

PHP

Sintaxis

Manejo De Fechas Y Tiempo

PHP

Sintaxis

Manejo De Arrays

PHP

Sintaxis

Introducción A La Poo En Php

PHP

Programación Orientada A Objetos

Clases Y Objetos

PHP

Programación Orientada A Objetos

Constructores Y Destructores

PHP

Programación Orientada A Objetos

Herencia

PHP

Programación Orientada A Objetos

Encapsulación

PHP

Programación Orientada A Objetos

Polimorfismo

PHP

Programación Orientada A Objetos

Interfaces

PHP

Programación Orientada A Objetos

Traits

PHP

Programación Orientada A Objetos

Namespaces

PHP

Programación Orientada A Objetos

Autoloading De Clases

PHP

Programación Orientada A Objetos

Manejo De Errores Y Excepciones

PHP

Programación Orientada A Objetos

Manejo De Archivos

PHP

Programación Orientada A Objetos

Patrones De Diseño

PHP

Programación Orientada A Objetos

Introducción A Los Formularios En Php

PHP

Formularios

Procesamiento De Datos De Formularios

PHP

Formularios

Manejo De Archivos En Formularios

PHP

Formularios

Redirecciones Y Retroalimentación Al Usuario

PHP

Formularios

Formularios Dinámicos Y Separación De Lógica

PHP

Formularios

Introducción A La Persistencia En Php

PHP

Persistencia

Conexión A Bases De Datos

PHP

Persistencia

Consultas Y Operaciones Crud

PHP

Persistencia

Gestión De Transacciones

PHP

Persistencia

Manejo De Errores Y Excepciones En Base De Datos

PHP

Persistencia

Patrones De Acceso A Datos

PHP

Persistencia

Concepto De Sesiones En Php

PHP

Sesiones Y Cookies

Configuración De Sesiones

PHP

Sesiones Y Cookies

Cookies

PHP

Sesiones Y Cookies

Manejo Avanzado De Sesiones Y Cookies

PHP

Sesiones Y Cookies

Principales Vulnerabilidades En Php

PHP

Seguridad

Seguridad En Formularios Y Entrada De Datos

PHP

Seguridad

Protección Frente A Inyección Sql

PHP

Seguridad

Gestión De Contraseñas Y Cifrado

PHP

Seguridad

Seguridad En Sesiones Y Cookies

PHP

Seguridad

Configuraciones De Php Para Seguridad

PHP

Seguridad

Introducción Al Testing En Php

PHP

Testing

Phpunit

PHP

Testing

Cobertura De Código En Testing

PHP

Testing

Test Doubles (Mocks, Stubs, Fakes, Spies)

PHP

Testing

Pruebas De Integración Y Funcionales

PHP

Testing

Accede GRATIS a PHP y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  1. Comprender el Patrón Repository y DAO en PHP.
  2. Implementar operaciones CRUD con clases DAO.
  3. Diferenciar entre acceso a datos y lógica de negocio.
  4. Aplicar el principio de separación de capas.
  5. Integrar repositorios en la lógica de negocio.