MVC Hecho en casa - Simple, lindo y efectivo

Este post va dedicado a mi amiga Piojita, que gracias a sus ganas de aprender tengo ánimos e ideas para escribir acá, ojalá puedan ser de utilidad a los que por miedo no preguntan

En estos días está muy de moda todo lo que sea MVC, que no es otra que escribir aplicaciones en tres niveles, Modelo (acceso a los datos), Vista (la presentación al usuario) y el Controlador (Encargada de los eventos, y en muchos casos es el nexo entre el modelo y la vista). Como Rasmus dice en su post The non-framework PHP MVC Framework, que en su época trajo polémica, utilizar frameworks tiene ventajas y desventajas, para mi gusto más desventajas que ventajas.

Primero que nada los frameworks webs convencionales, realizan muchas tareas automáticamente, y abstrae muchos detalles al programador que si bien ahora un montón de tiempo, no es efectiva, ya que asúme varías cosas por defecto. Cuando digo que no es efectiva hablo de que lo que se gana en tiempo, se pierde en el rendimiento de la aplicación, ¿de que nos sirve hacer una aplicación web en 5 minutos si sólo puede tener hasta 5 visitas en simultáneo? ¿No sería acaso mejor tardar 40 minutos y que soporte 30 visitas en simultáneo?, quizá  parezca una  exageración pero se acerca mucho a la realidad.

No estoy diciendo que utilizar frameworks es algo malo, depende de las necesidades del sitio web a ser realizado. <ironic>Si fuésemos radicales cada uno escribiría programas en asembler que corra con en nuestro propio sistema operativo,</ironic>

Mi idea con esta entrada es mostrar que no sólo utilizando frameworks que fuerzan a uno a utilizar uno u otro modelo de programación es posible escribir aplicaciones web en poco tiempo manteniendo la simpleza y mantenibilidad del código como primer meta. En otras palabras escribiré, o al menos esbozaré, un mini-framework MVC en PHP.

Modelo

El modelo es, probablemente, la parte más delicada del sistema ya que es la encargada de interactuar con las bases de datos. Siempre es bueno diseñar una buena API de datos persistentes, para no tener que escribir SQL en todas partes. Aparte de mantener el código ordenado, ayuda a la legibilidad del mismo, ya que sabemos si hay un error en base de datos, sabremos con seguridad de que parte del código hay que parchar.

A mi me gusta usar clases para definir una tabla y relaciones, sería algo así como lo siguiente (no es código funcional, solo un ejemplo).

<?php
class Users {
    function GetAll() {
        return $this->Select();
    }

    function Select($params=array()) {
        $sql = "SELECT * FROM users";
        if (is_array($params) && count($params)) {
            $where=array();
            foreach($values as $col=>$val) {
                $where[] = "$col='".addslashes($val)."'";
            }
            $sql .= " WHERE ".implode(" and ",$where);
        }
        return db_exec($sql);
    }

    function Update($id,$values) {
        if (!is_numeric($id)||!is_array($values)||!count($values))
            return false;
        $id  = addslashes($id);
        $cols = "";
        foreach($values as $col=>$val) {
            $cols .= "$col='".addslashes($val)."',";
        }
        $cols[strlen($cols)-1] = ' '; /* borrar el ultimo , */
        $sql = "UPDATE users SET $cols WHERE id = $id";
        db_exec($exec); /* call some db */
    }
}

?>

Algo un poco más automático es esta parte sería grandioso, es un poco molesto escribir mucho SQL, especialmente para las relaciones y esas cosas. Actualmente estoy trabajando, con un amigo brasilero, en una implementación de ActiveRecord (con API similar al de AppEngine) para PHP 5 (es que quiero hacer algo útil también con PHP5). No voy a dar más detalles hasta que esté terminado, pero doy la primicia, se podrán definir relaciones en las clases. Ejemplo:

<?php
class Author extends XXORM {
    function data() {
        $this->name = DB::String(array("size"=>50,"required"=>true));
        $this->book = DB::Relation("Books",DB::MANY);
    }
}

class Books extends XXORM {
    function data() {
        $this->author = DB::Relation("Author",DB::ONE);
        $this->title = DB::String(array("required"=>true,"size"=>50));
    }
}
?>

El proyecto, creara las estructuras de las tablas (si se ejecuta en modo development), también vendrá con una API para extender utilizando Hooks, especial para el cacheado de consultas y demás. Se podrán crear filtros de validaciones por campos, entre otras caracteristicas, si están interesados pueden seguirme twitter

La vista

Una de las características de PHP es que puede ser mezclada con HTML, pues entonces ¿para que el overhead de utilizar un template engine?

<html>
<head>
    <title><?php echo $title?></title>
</head>
<body>
    <?php foreach($users as $user): ?>
        <?php echo $user?>
    <?php endforeach;?>
</body>
</html>

Algunos consejos, evitar los short tags(<?=, <?), evitar utilizar las llaves ({}) para ello utilizar los : y los end*; asi como el ejemplo.

El controlador

El controlador no es nada mas que una función que la mayoría del tiempo llama a la vista y al modelo, aunque no se limíta solamente a eso (ej. enviar un e-mail, subir un archivo de un a ftp, etc).

<?php

function user_controller($params) {
    if (
            !is_array($params) ||
            !isSet($params[1]) ||
            !is_numeric($params[1])
        )
            return false;
    /* llamamos el modelo */
    $dbu   = new User;
    $users = $dbu->Select(array("id"=>$params[1]));
    /* incluimos la vista */
    /* la variable $users es visible para vista */
    include("vista.php");
}

?>

Ruteo de URL a Controladores

<?php
class Router {
    function AddRoute($reg_expr,$action) {
        if (!is_callable($action) && !is_file($action))
            return false;
        $this->route[$reg_expr] = $action;
    }

    function FindAction($url) {
        $routes = & $this->route;
        foreach((array)$routes as $route) {
            extract($route);
            if (preg_match($reg_expr,$url,$params)) {
                $ret = true;
                if (is_callable($action))
                    $ret = call_user_func($action,$params);
                else
                    include($action);
                return true;
            }
        }
        if (!isset($ret) && !$ret)
            $this->err();
        return false;

    }

    function err() {
        header("HTTP/1.1 404 Not Found");
        include("not-found.php");
    }
}

?>

Poniendo todo en orden

Hasta ahora vimos el MVC, separados, ahora ¿como juntamos todo eso?, pues sería algo así

<?php
/* la clase de ruteo URL -> Controlador*/
include("routes.php");
/* los modelos de datos */
include("model.php");
/* controladores */
include("controllers.php");

/* */
$router = new Router;
/* ruta hacia un controlador */
$router->addRoute('/user\/([0-9]+)$/i',"user_controller");
/* ruta hacia una vista, por no se necesite un */
/* controlador */
$router->addRoute("/","principal.php");
#... Demas reglas

/*
 * buscamos el controlador, basado en el URI,
 * suponiendo que tenemos el URL Rewrite activado
 * un uri com "/user/11" haria match, y se ejecutaria
 * un controlador.
 */
$route->FindAction($_SERVER['REQUEST_URI']);
?>

Como verán, un poco de orden en la forma de programar, utilizando un mini-framework, que en realidad son buenos hábitos a la hora de programar, fácilita nuestro trabajo (y de los que mantendrán nuestro código), a la vez de hacerlo efecto (¿para que derrochar valiosos ciclos de procesador?).

10 Comments

  1. piojita says:

    Tengo que agradecerte por toda la ayuda que me estas dando, al principio cuando empecé a interesarme por el MVC despues de leer uno de los tantos articulos sobre “Coding for the Good of The Team” y me dispuse a desarrollar el primer proyecto basado en MVC valga la redundancia :P busqué y leí varios articulos pero nada me qedaba tan claro como cuando te escribia al gtalk a cuestionarte sobre tu codigo :P y sobre como hiciste esto o aquello.. gracias a todo pude aprender bastante! todavia habian algunas cosas que quedaron flotantes por ahi, te conté acerca de lo de los articulos, entonces prometiste escribir alguno para aquella gente que este con ganas de aprender.. Y la verdad q despues de tanto buscar tengo q decirte q es uno de los q mejor explicados esta!! o no sé si es por el hecho de que siento que fue tan escrito a mis necesidades que quedó perfecto. Muchas Gracias Ce! enserio.

  2. Matías says:

    ¡Mini-framework excelente!
    Ya quiero ver la implementación de ActiveRecord que estan cocinando ;-)

  3. max says:

    el famoso simple, bonito y barato.. esta bueno! :D

  4. Guille says:

    ¡Genial articulo! Yo estoy empezando desarollo web en Java y todavia no probe usar un framework MVC, pero voy a probar un desarollo MVC a ver que tal.

    Excelente blog, me dan ganas de probar PHP e intensificar con Javascript.

  5. marcelo says:

    que piensas de codeigniter? es tambien un framework mvc..

  6. En el trabajo (www.publico.es) utilizamos para páginas sencillitas y rápidas que sólo van a tener una vida de unos pocos días lo que en broma llamamos el “MVC monolítico”:
    Simplificando algunos detalles:

    Para aplicaciones grandes, con muchas funcionalidades y en las que los requisitos son cambiantes sí que me parece útil un FW (me gusta Symfony). Si juegas con las cachés el penalty en rendimiento puede ser aceptable.

    Un saludo.

  7. [...] a mi post sobre MVC hecho en casa, que en mi humilde opinión fue útil, ya que hubo muchos comentarios y correos electrónicos sobre [...]

  8. ronal says:

    Esta muy bueno, sigue adelante

  9. aadsfsfdfas says:

    Te faltó una “r” más en “ahora”

Leave a Reply