Newsletter para devsEntra

Refactorizar una aplicación legacy. Parte III: Base de datos, rutas y vistas

Ya nos vamos acercando al final de la refactorización de nuestra aplicación legacy.

Si habéis venido a parar a este artículo directamente, os invito a visitar nuestras dos anteriores entregas donde nos sumergimos en los puntos más importantes para modernizar una web con código legado:

Refactorizar una aplicación legacy. Parte I: Spaghettis y POO

Refactorizar una aplicación legacy. Parte II: Desenredando la madeja

En esta ocasión vamos a dar la última puntada con tres aspectos de nuestra aplicación web a refactorizar:

  • La abstracción de la capa de base de datos.
  • La gestión de rutas.
  • La creación de vistas.

Refactorizar aplicación legacy (III) - abstraccion base datos

Abstraer la Base de Datos es importante

Tal y como habíamos visto en los anteriores artículos las llamadas se realizaban con sentencias en SQL, contra una base de datos MySQL y con funciones propias del lenguaje (en este caso PHP).

Además, nos estábamos enfrentando a una aplicación que contenía las llamadas a base de datos dentro de cada uno de los archivos de acción correspondiente:

  • contactos.php: para la consulta de contactos.
  • annadir.php: realizaba la inserciones de los contactos.
  • modificar.php: encargado de la modificación de registros.

Y así sucesivamente.

Cómo bien podemos imaginar, las consultas a base de datos, no impedían que ataques del estilo SQL injection se pudieran ejecutar.

Por otro lado, las consultas a contactos se suceden en muchos de los archivos, repitiendo sucesivamente consultas que podrían hallarse en un solo lugar.

Pero, ¿cómo podemos gestionar todo este embrollo de una manera más eficaz?

Si simplemente queremos una gestión de sentencias SQL más eficiente, más fácil y más segura, basta con integrar un constructor de sentencias.

En cada lenguaje tenemos una gran variedad de opciones, uno de los mas famosos en PHP es DBAL - SQL Query Builder perteneciente al paquete de soluciones ORM de Doctrine.

Doctrine implementa un paquete muy completo de soluciones para la abstracción de la base de datos de tu proyecto, no dudes en visitar su página web oficial si estás interesado.

Una vez instalado Doctrine en nuestro proyecto (se puede instalar tal y como lo hemos hecho en artículos anteriores, con Composer), podremos tener acceso a lanzar el método createQueryBuilder para crear nuestra primera Query.

Vamos a ver la nomenclatura que usaríamos para obtener el contacto con id=1 de nuestra base de datos:

<?php

$userID = 1;

$queryBuilder
    ->select('name')
    ->from('contacts')
    ->where('id = ?')
    ->setParameter(0, $userID);

Como podéis apreciar, el valor parámetro no se inserta directamente después de id=, sino que viene dado por el valor de una variable. Esto evita los ataques SQL Injection.

Mucho más legible, ¿verdad? Esta es otra de las ventajas que obtenemos con el constructor de sentencias SQL.

En este punto no tendríamos ninguna dificultad para insertar un método dentro de nuestra clase de contactos (ubicada en src/Contacts.php) y así obtener todos los datos de un contacto desde un sólo punto.

Veamos cómo implementar esta funcionalidad:

<?php

namespace ourAPP;

class Contacts{

    public function getContactByID($id){
        $userID = $id;
        return  $queryBuilder
                    ->select('*')
                    ->from('contacts')
                    ->where('id = ?')
                    ->setParameter(0, $userID);        
    }

}

Igualmente DBAL implementa soluciones para insertar registros, modificarlos, etc.

SlimPHP: Enrutando de una manera eficiente

Aunque existen una gran cantidad de librerías para la implementación tanto de rutas como de vistas, en este caso vamos a utilizar un pequeño framework de PHP: SlimPHP.

Para instalar SlimPHP con Composer tenemos que ejecutar el siguiente comando:

composer require slim/slim:'4.*'

Igualmente necesitamos la implementación del estándar PSR-7 para la gestión de peticiones y respuestas HTTP:

composer require slim/psr7

Ahora, en la puerta de entrada de nuestra aplicación (normalmente en public/index.php), arrancaremos la aplicación SlimPHP:

<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

Ya lo tenemos todo listo para empezar a crear nuestras rutas.

El gestor de rutas de SlimPHP nos permite la integración de todos los métodos de ataque PHP: GET, POST, PUT, etc.

Probemos a integrar la ruta que nos devolverá todos los contactos de la base de datos en la ruta /contacts a través del método GET:

<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use src\Contacts.php;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->get('/contacts', function ($request, $response) {

    $contacts = new Contacts();
    return $contacts->getAllContacts();

});


Por último, intentemos la misma acción pero esta vez recibiendo el ID de contacto como parámetro en la URL:

<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use src\Contacts.php;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->get('/contacts/{id}', function ($request, $response, array $args) {

    $contacts = new Contacts();
    return $contacts->getContactByID($args['id']);

});

Con esta aproximación, podremos atacar a la ruta de este modo /contacts/1, para así obtener el contacto con id=1.

Fijaos como la función anónima recibe los argumentos de la dirección web para poder ser utilizada dentro mediante $args['id'].

SlimPHP: Vistas, el lazo final del MVC

Por último sólo nos queda tener una forma de poder mostrar por pantalla la información al usuario. Lo que llamamos interfaz o frontend.

Esta funcionalidad se consigue con la implementación de vistas.

Refactorizar aplicacion legacy (III) - Vistas

En este caso vamos a utilizar la librería PHP-View para slimPHP.

Para su instalación mediante Composer ejecutaremos el siguiente comando en consola:

composer require slim/php-view

Finalmente, imaginemos que tenemos nuestras plantillas almacenadas en la carpeta raíz /templates y que dentro tenemos la plantilla contact.php. Esta plantilla nos mostrará el nombre del contacto con el ID que se haya pasado como parámetro a la ruta /contact.

Vamos a ver un ejemplo de lo que podría contener nuestra plantilla /templates/contact.php:

<html>
    <head>
    </head>
    <body>
        <h1><?php echo $nombre; ?></h1>
    </body>
</html

Como podéis ver, dentro de la vista, podemos utilizar la variable que la plantilla recibe como parámetro.

Vamos a ver cómo realizar la llamada a la plantilla y el paso de parámetros a la misma.

En el archivo index.php donde creamos nuestra aplicación Slim, sólo tendremos que hacer una referencia a PhpRenderer y una inclusión de la plantilla dentro de la ruta elegida:

<?php
use Slim\Views\PhpRenderer;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use src\Contacts.php;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->get('/contacts/{id}', function ($request, $response, array $args) {

    $contacts = new Contacts();
    $contactName = $contacts->getContactName($args['id']);
    
    $renderer = new PhpRenderer('/templates');
    return $renderer->render($response, 'contact.php', $contactName);

});

En este caso, si atacamos a la dirección: https://nuestrodominio.domain/contact/1, nos devolvería el nombre del contacto insertado dentro de una cabecera <h1>.

Y llegamos al final

Ahora sólo me queda agradeceros haber seguido esta serie de artículos en el que hemos refactorizado una aplicación legada. Una ardua tarea que conlleva muchas satisfacciones una vez finalizado el trabajo.

Recordad que hemos pasado por:

  • Cambio de lógica de programación procedural a orientación a objetos.
  • Implementación de un modelo MVC.
  • Integración de un gestor de librerías.
  • Cambio en la estructura de carpetas.
  • Abstracción de la capa de base de datos.
  • Gestión de rutas.
  • Implementación de vistas.

Y no olvidéis que implementar esta buenas prácticas no sólo os ayudará a ser ‘programadores actualizados’, sino que además conllevará un más fácil mantenimiento de vuestra aplicación, además de mejorar su seguridad.

Si queréis ampliar los conocimientos adquiridos durante esta saga os invito a que escuchéis el podcast de la escuela online de programación de Daniel Primo:

Refactorizar una aplicación legacy (Parte 2)

¡Nos vemos en el siguiente fragmento de conocimiento, equipo!

Escrito originalmente por: Juan José Ramos

Imagen de Daniel Primo

Daniel Primo

CEO en pantuflas de Web Reactiva. Programador y formador en tecnologías que cambian el mundo y a las personas. @delineas en twitter y canal @webreactiva en telegram

12 recursos para developers cada domingo en tu bandeja de entrada

Además de una skill práctica bien explicada, trucos para mejorar tu futuro profesional y una pizquita de humor útil para el resto de la semana. Gratis.