Por qué voy a hacerme una arquitectura MVC en PHP

Soy un tío con un montón de proyectos siempre en la cabeza y se me ha metido esta vez realizar un juego social online. Llevo ya mucho tiempo dedicando unas horas a la semana a complementar mis conocimientos y experiencia. Aprovecho estos proyectos paralelos para aprender más y además buscando algún tipo de beneficio económico.

La cosa es que me bajo del mundo SEO porque no tengo tiempo suficiente para realizar un proyecto que de frutos y me voy a dedicar una temporada a crear un producto software. Las ventajas son que puedo crecer a mi ritmo (dispongo de pocas horas a la semana) y que me gusta mucho más programar, por lo que como afición prefiero desarrollar cosas.

Bueno, este es un artículo de programación avanzado y creo que es necesario, para sacarle jugo, saber qué es AJAX para algún ejemplo, tener claro cómo se programa con capas y con lenguajes orientados a objetos.

Escoger un framework PHP

He trabajado durante 15 años en el mundo Java y recientemente me he pasado al otro lado y ahora trabajo en .NET. Pero el PHP tiene para mí un regusto a los días de Universidad que me pone contento y en sintonía. Me ayuda a ser creativo.

PHP además es el lenguaje en el que se ha desarrollado WordPress y por lo tanto, el lenguaje que rula el mundo Web.

Como el proyecto que me planteo es grande, he empezado por buscar frameworks PHP y lo que he encontrado son entre otros Laravel, Symphony, CakePHP o CodeIgniter.

Nombro aquí los que me acordaba de haber visto en ofertas de empleo, pero como os podéis imaginar, hay decenas.

Casi todos están basados en un esquema MVC al que estoy acostumbrado del mundo Java y con el que también me estoy pegando en .NET.

Ventajas (para mí) de estos frameworks es que están bastante solicitados en los puestos de desarrollo, pero es un punto que ahora mismo no me interesa: yo curro en .Net y espero que sea para largo.

Desventaja es que tienen una curva de aprendizaje para la que necesito un par de jornadas libres para centrarme en ellos. Y como ya dije: eso es un hobbie. Es un hobbie con pretensiones, sí. Ojalá acabe todo esto en algo.

Pero no dispongo de muchas horas y mucho menos de horas seguidas para dedicarle a mis proyectos paralelos.

Así que dí con un artículo de Victor Robles @victorobs sobre hacer una arquitectura MVC en PHP desde cero. Y mira, yo de MVC piloto y tampoco es la primera vez que me monto algo Juan Palomo – one-man-project –  así que voy a ver qué sale de ésto y voy a tirar por la vía casera.

Modelo de capas básico

Para explicar en qué consiste esto voy a empezar por contar cómo hice el último proyecto web en PHP, que por otra parte, es como te enseñan en casi todos los sitios, en la Uni en 1998 (Dios, hace 20 años) pero también en casi todos los cursos habidos y por haber de PHP.

Uno tiene una página PHP que contiene parte en lenguaje PHP que se ejecuta en servidor y parte HTML que sale en el navegador.

Este es un ejemplo de un fichero de otro proyecto (2010) que no llegó a ver la luz. Se trataba de asignar un deportista a un partido a través de una llamada ajax. Ten en cuenta que se trata del fichero PHP al que llama la función ajax:

http://buscapartido.com/asistencia.php

<?php

// aquí recupero los parámetros del formulario
 
 $match_id = $_POST["match_id"];
 $player_id = $_POST["user_id"];
 $newstate = $_POST["newstate"];
 $wf = PlayerWF::getWF();
 
 global $current_user; 
 $user_id = $current_user->ID;
 
// hay que realizar una serie de operaciones sobre el usuario que las
// hace una capa controladora (negocio) que lógicamente tengo 
// en un fichero aparte.
 
 if (Controller::updatePlayer ($match_id, $player_id , $newstate) > 0) 
 { 

// tenemos una capa DAO que se encarga de recuperar los datos del
// partido. Pero fíjate que la capa DAO la estoy llamando desde 
// el fichero asistencia.php

 $dao = new MatchDao ();
 $match = $dao->get_match ($match_id);
 
 $result = array("cod_respuesta"=>"1","txt_respuesta"=>"ok");
 $result ['estado'] = $newstate;
 $result ['stateTitle'] = $wf[$newstate]['title'];
 $result ['stateHelp'] = $wf[$newstate]['help'];
 $result ['match_id'] = $match_id;
 $result ['player_id'] = $player_id;
 $result ['master_id'] = $match->responsable;
 $result ['esAdmin'] = (($user_id == $match->responsable)?"S":"N");
 }
 else 
 $result = array('cod_respuesta'=>'0','txt_respuesta'=>'Error al actualizar al guardar los datos');
 
// una vez hechas las operaciones, llamo a una función que transforma
// el modelo en vista: el array de valores en JSON 
// (repito: es la salida de un ajax)

 echo ModelView::array2json ($result);
 
?>

Bueno, esto es un esquema por capas en el que el navegador hace la petición directamente a una página PHP que es la que hace de orquestador y llama a la parte de negocio, a la capa de datos y a la vista.

Está muy bien (esto se enseña en todos lados) que se separa el acceso a datos (Data Access Objects) de lo que es la lógica de negocio (controlador) y de la transformación para el usuario (en este caso, es un ajax, que devuelve un JSON a partir de un array).

Modelo de capas

Una mejora a esto es que la capa DAO esté llamada directamente desde controlador (por debajo del controlador) y que la asistencia no se entere de dónde provienen los datos.

No hay contrato entre el controlador y la página se usan directamente los parámetros de POST (lo que llega del formulario).

Mi objetivo

Bueno, vamos a ver qué queremos hacer ahora.

Ahora lo que quiero es que en vez de pedir una página así:

http://miproyecto.com/asistencias.php?accion=alta&usuario=pepe&etc=etc

Pedir una página de este tipo:

http://miproyecto.com/asistencias/alta

«Pero puedes mandar los datos por POST y no se ven en el Query String»
No te quedes en ese detalle. Es un ejemplo

Yo busco centrarme en este concepto: en la petición de arriba yo me dirijo a una página concreta, asistencias.php que se tiene que encargar de todo (seguridad, negocio – en el ejemplo hasta la capa de datos, vista, etc) y tengo mezclado todo en código espaguetti, todas estas facetas.

En este ejemplo, como devuelve JSON la parte de vista se resuelve en una llamada pero si fuera HTML ya sabemos que estaría metido por el medio de las llamadas de backend.

Yo quiero tener URL amigables del tipo 2 (como un path) y esto lo transformo yo con ayuda del servidor web en una llamada a una php con sus parámetros. Como voy a alojar mi nuevo proyecto en un Apache, pues un htaccess como éste:

RewriteEngine on
RewriteBase /soga/
RewriteRule ^([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)\/?$ /soga/index.php?control=$1&accion=$2

Lo que gano con ésto es que al usuario no le enseño qué página es la que sirve la petición (aunque lo puede sospechar) pero se va a llevar un chasco porque observa: index.php

index.php querido amigo: todas las peticiones van a entrar por el mismo sitio, que es donde voy a controlar la seguridad (autorización, roles, etc).

Voy a montar un MVC lo mejor que pueda basándome en el artículo de MVC en PHP desde cero de Victor. Que es un artículo de 2014 y que sí, puede mejorar, pero que como base me vino genial. Yo voy a tratar de aproximarlo un poco más a cómo funciona MVC en .Net.

Un MVC normal y corriente.

Esto es un modelo sencillo de MVC en el que el navegador hace una petición al controlador, que realiza una serie de llamadas al modelo en busca de datos (los  recupera de la Base de Datos).

Con esos datos y los que tiene del navegador hace unas operaciones y el resultado lo manda a la vista con un formato concreto que llamaremos Contrato pues es en lo que han quedado el controlador y la vista que iban a pasarse.

La verdad, que se parece mucho al gráfico anterior, pero el otro fue construido modularmente, pensando más en organizar el código que en tener capas separadas.

MVC SIMPLE

Un MVC hormonado

Vale, esto es una aproximación básica pero lo que yo quiero hacer en realidad es esto:
mvc ampliado

El navegador va a llamar a un sólo fichero director, que se va a encargar de cosas generales como la seguridad, mantener la sesión etc. Este va a encargarse de determinar qué quiere hacer el usuario en base a la url de llamada:

http://miproyecto.com/asistencias/alta

A partir de aquí va a llamar a un objeto Controlador que se encargará de la parte de negocio, llamará al modelo para recuperar los objetos (en este caso, asistencias de un jugador a un partido).

El Controlador realizará la operación de Alta que se está solicitando y obtendrá un resultado (vamos a decir, la lista de jugadores que tienen asistencia a dicho partido). Este conjunto de datos es un Modelo con objetos (Entidades) que representan filas en la base de datos. El Modelo es directamente el contrato entre Vista y Controlador.

En este momento se elije una vista, que será un fichero HTML o JSON o WebService, u otra cosa todavía por inventar, capaz de interpretar dicho modelo (una simple representación de los datos del Modelo sin contener negocio).

Que puede ser un pelín difícil de entender, pero que esto ya está codificado y de verdad que ha quedado chachi.

En la próxima os lo cuento.

Scroll al inicio