Jun 06

El Modelo
La ‘M’ de los sistemas MVC se refiere al modelo, es responsable de ejecutar consultas a la base de datos (u otra fuente externa como pueden ser archivos xml, csv, etc.) y proveer los datos al controlador. Nosotros debe cargar el modelo apropiado dependiendo de la solicitud de nuestro cliente, algunas personas prefieren no utilizar una clase modelo y utilizar en el controlador una librería que haga la abstracción de la BDD, como por ejemplo AdoDB, sin embargo el modelo también puede hacer uso de esta librería o utilizar las funciones nativas de php directamente. Todo depende del tamaño de la aplicación y lo práctico que resulte en la solución que propongamos

Algo que debemos hacer es agregar el código necesario para inicializar una conexión con la base de datos, y añadirlo a nuestra pagina inicial (index). Existe varias librerías de abstracción de la BDD disponibles, pero PHP5 ya viene con una gran librería PDO, entonces es posible que no necesitemos alguna más.

Ahora, colocamos el siguiente código en el archivo index (luego de incluir el archivo de inicialización):

# Conexión con la BDD
$db = new PDO('mysql:host=localhost;dbname=demo', '[usuario]', '[clave]');
$registry->set ('db', $db);

En el ejemplo anterior primero creamos una nueva instancia de la libreria PDO, y nos conectamos a nuestra base de datos MySQL. Posteriormente hace global la conexión mediante nuestra clase Registry.

La parte del modelo de nuestro sistema, esta terminada, por lo que vamos a pasar con con la siguiente parte: codificar el controlador.
Codificar el controlador también significa que nosotros debemos codificar la clase Router. Una clase Router es responsable de cargar correctamente el controlador, basada en las solicitudes (la variable $router pasada a través de la URL). Entonces vamos a codificar la clase Router primero.

La clase Router
Nuestra clase Router deberá analizar la solicitud del cliente y cargar el comando correcto. El primer paso es crear el esqueleto básico de la clase Router:

Class Router
{
private $registry;
private $path;
private $args = array();

function __construct($registry) {
$this->registry = $registry;
}
}

Y agregamos las siguiente líneas al archive index.php:

# Carga el router
$router = new Router($registry);
$registry->set ('router', $router);

Ahora hemos añadido la clase Router a nuestro sistema MVC, pero esta no hace nada aún, entonces debemos agregar varios métodos a nuestra clase.
El primer metodo que debemos agregar es setPath(), el cual es utilizado para definir el directorio donde se almacenan todos nuestros controladores. El metodo setPath() es similar al siguiente y require ser agregado a la clase Router:

function setPath($path)
{
$path = trim($path, '/\\');
$path .= DIRSEP;
if (is_dir($path) == false) {
throw new Exception ('Path Invalido: `' . $path . '`');
}
$this->path = $path;
}

Nota: Si su sistema no tiene la constante DIRSEP, prueben con DIRECTORY_SEPARATOR

Luego, agregamos la siguiente línea en el archivo index.php:

$router->setPath (site_path . 'controllers');

Ahora nosotros ya tenemos definido el path a nuestros controladores, entonces podemos escribir el método responsable de cargar el controlador correcto. Este método es llamado delegate(). Una primera versión de este método puede ser como sigue:

function delegate()
{
// Obtiene el controlador
$this->getController($file, $controller, $action, $args);

Como pueden ver, hace uso de otro método, getController() para obtener el controlador. Este método sería algo así:

private function getController(&$file, &$controller, &$action, &$args)
{
$route = (empty($_GET['route'])) ? '' : $_GET['route'];
if (empty($route)) { $route = 'index'; }
// Separa las partes
$route = trim($route, '/\\');
$parts = explode('/', $route);
// Busca el controlador correcto
$cmd_path = $this->path;
foreach ($parts as $part) {
$fullpath = $cmd_path . $part;

// Existe un directorio con el path completo?
if (is_dir($fullpath)) {
$cmd_path .= $part . DIRSEP;
array_shift($parts);
continue;
}
// Busca el archivo
if (is_file($fullpath . '.php')) {
$controller = $part;
array_shift($parts);
break;
}
}
if (empty($controller)) { $controller = 'index'; };
// Obtiene la accion
$action = array_shift($parts);
if (empty($action)) { $action = 'index'; }
$file = $cmd_path . $controller . '.php';
$args = $parts;
}

Veamos un poco el funcionamiento de este método. Primero obtiene el valor de la variable $route y procede a separarla en partes, utilizando la función explode(). Si la solicitud es ‘miembro/ver’ esto debería dividirlo en un arreglo: array(‘miembro’, ‘ver’).

Podemos utilizar el loop foreach para recorrer cada parte y primero chequear si cada parte es un directorio. Si lo es, lo agregamos en el filepath y nos movemos a la siguiente parte. Esto permite poner al controlador en subdirectorios, y utilizar jerarquía en los controladores. Si una parte no es un directorio pero es un archivo, almacenamos esto en la variable $controller, y salimos del loop por que hemos encontrado el controlador que queríamos.

Luego del loop nos aseguramos que el controlador ha sido encontrado, y si no existe un controlador ponemos por omisión a uno llamado ‘index’.
Procedemos a obtener la acción que deseamos ejecutar. El controlador es una clase que se compone de varios métodos, y las acciones apuntan a uno de estos métodos. Si no se especifica una accione llamaremos a una acción llamada ‘index’.

Finalmente, obtenemos el path complete del archive controlador concatenando el path con el nombre del controlador y su extensión.

Ahora que la solicitud ha sido analizada se la envía al método delegate() para cargar el controlador y ejecutar la acción. El método delegate() complete sería algo así::

function delegate()
{
// Analiza la ruta
$this->getController($file, $controller, $action, $args);
// El archivo existe?
if (is_readable($file) == false) {
die ('404 Not Found');
}
// Incluye el archivo
include ($file);
// Inicializa la clase
$class = 'Controller_' . $controller;
$controller = new $class($this->registry);
// Existe la Accion?
if (is_callable(array($controller, $action)) == false) {
die ('404 Not Found');
}
// Ejecuta la acción
$controller->$action();
}

Luego de tener analizada la solicitud con el metodo getController(), primero debemos asegurarnos que el archivo existe y si no retornar un sencillo mensaje de error.

La siguiente cosa que debemos hacer es incluir el archivo controlador, e inicializar la clase, que siempre debe ser llamada Controller_[nombre]. Luego aprenderemos mas sobre el controlador.

Entonces verificamos si la acción existe y es ejecutable utilizando is_callable(). Finalmente, ejecutamos la acción, que completa el rol del router.

Ahora que tenemos funcionando completamente el método delegate(), agregamos las siguientes líneas al archivo index.php:
$router->delegate();

Si intentan ejecutar el archivo ahora, obtendremos el siguiente error por que no tenemos creado el directorio ‘controllers’:

Fatal error: Uncaught exception 'Exception' with message 'Invalid controller path: `g:\Projects\PHPit\content\simple mvc php5\demo\controllers\`' in g:\Projects\PHPit\content\simple mvc php5\demo\classes\router.php:18 Stack trace: #0 g:\Projects\PHPit\content\simple mvc php5\demo\index.php(13): Router->setPath('g:\Projects\PHP...') #1 {main} thrown in g:\Projects\PHPit\content\simple mvc php5\demo\classes\router.php on line 18

O obtendremos ‘404 Not Found’ porque no tenemos controladores creados aun. Pero ese es el tema de la siguiente parte de este tutorial.

Abr 22


Resumen


En este tutorial usted va a aprender como construir un sistema Modelo-Vista-Controlador simple con PHP 5.1 y algunas de las características de la Biblioteca Estándar de PHP (SPL’s).


Introducción


Bienvenido al primer tutorial de PHP5, usted necesitará tener PHP 5.1y SPL instalado para seguir este tutorial, esto debido a que utilizaremos varias de las ultimas características existentes en PHP5
En este tutorial voy a mostrarles como construir un sistema Modelo-Vista-Controlador simple, el cual es el patrón de diseño más común para el desarrollo de grandes aplicaciones Web. Voy a guiarte por todos los pasos necesarios para iniciarte en el mundo de un verdadero sistema MVC


Un punto de entrada


Una aspecto importante relacionado con nuestro sistema MVC es que éste debe tener un único punto de entrada. Envés de tener varias docenas de archivos PHP que realicen lo siguiente:

include ('global.php');
// Aquí el código de la pagina actual
?>

Tendremos una sola página que maneje todas las peticiones. Esto significa que no tendremos que incluir el archivo global.php cada vez que deseamos crear una página nueva. Este “único punto de entrada” será llamado “index.php” y, hasta el momento, será algo así:

// Hace alguna cosa
?>

Como se puede apreciar, la página principal no hace nada todavía, pero lo hará en un minuto.
Para cerciorarse de que todas las peticiones vayan a la página principal configuraremos RewriteRule en el archivo .htaccess utilizando para ello el modulo de apache mod_rewrite. Debemos colocar el archivo llamado “.htaccess” en el mismo directorio que se encuentra el archivo de index.php y debe incluir el siguiente código:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?route=$1 [L,QSA]

Primero comprobamos si existe el archivo actual con el comando RewriteCond, y si no existe, lo redireccionamos al archivo index.php. Tenemos que comprobar si el archivo existe, porque también deseamos poder utilizar archivos normales que no son PHP, tales como imágenes JPEG.

Si usted no puede utilizar .htaccess o el modulo mod_rewrite, debería realizar las redirecciones manualmente a través de peticiones al archive index.php, lo cual significa que sus enlaces deben seguir el formato “index.php?route=[petición-va-aquí]“, ejemplo: index.php?route=chat/index.

Ahora que todas las peticiones están pasando a través de un solo punto de entrada, podemos comenzar a escribir el archivo index.php. Las primeras cosas que tendremos que realizar son algunas tareas inicialización. Para ello creamos un nuevo directorio llamado “includes”, y en este directorio creamos un nuevo archivo llamado “startup.php”. Luego debemos poner el siguiente código en el archivo index.php:

# Tareas de inicialización (definición de constantes, etc.)
require 'includes/startup.php';

Feb 26

Tareas de inicialización

Las tareas de inicialización son utilizadas para realizar actividades generales de inicialización como definir constantes, configurar el nivel de reporte de errores, etc. La primera parte del archivo de inicialización debe verse de cómo lo esto:

  1. <?php
  2. error_reporting (E_ALL);
  3. if (version_compare(phpversion(), '5.1.0', '<') == true) { die ('PHP5.1 Only'); }
  4. // Constants:
  5. define ('DIRSEP', DIRECTORY_SEPARATOR);
  6. // Get site path
  7. $site_path = realpath(dirname(__FILE__) . DIRSEP . '..' . DIRSEP) . DIRSEP;
  8. define ('site_path', $site_path);

En el ejemplo anterior nosotros: definimos algunas constantes, obtenemos el path del sitio y también nos aseguramos que la versión actual de PHP sea por lo menos 5.1.

La siguiente tarea que debemos realizar es configurar un objeto Registry para colocar toda la información que tenga características de global. Un objeto Registry es transmitido por todos los objetos en nuestro sistema MVC y es utilizado para compartir la información global sin tener que utilizar la palabra reservada de PHP ‘global’ o $GLOBALS. Para mayor información leer “Using globals in PHP”.

Agregar el siguiente código en el archivo startup.php, a continuación del código del ejemplo anterior:

$registry= new Registry;

Si trata de ejecutar el sistema ahora, usted debería obtener el siguiente error:

Fatal error: Class 'Registry' not found in g:\Projects\PHPit\content\simple mvc php5\demo\includes\startup.php on line 12

En realidad no es una sorpresa, ya que nosotros no tenemos creada la clase Registry aún, y tampoco tenemos incluido un archivo que contenga la clase Registry. Nosotros podríamos simplemente incluir el archivo utilizando la función include(), pero realizaremos esto utilizando una de las nuevas características de PHP5: __autoload().

La función mágica __autoload() es usada dinámicamente para cargar clases. Siempre que PHP encuentre que la clase no existe, primero llamará a la función __autoload(), y solo después mostrará un error. Esto puede ser utilizado para cargar clases “al vuelo”

Colocar el siguiente código antes del código del ejemplo anterior:

  1. // For loading classes
  2. function __autoload($class_name) {
  3. $filename = strtolower($class_name) . '.php';
  4. $file = site_path . 'classes' . DIRSEP . $filename;
  5. if (file_exists($file) == false) {
  6. return false;
  7. }
  8. include ($file);
  9. }

Nuestra función __autoload() toma el nombre de la clase, pasada como un argumento, y chequea si existe una clase llamada de manera similar en nuestro directorio de clases. Si el archivo no existe, la función retornara false y un error fatal será mostrado, pero si el archive existe, este será incluido, lo cual significa que la clase estará disponible y no se lazará un error.

Nosotros aun no tenemos creado una clase Registry, lo que significa que aun obtendremos un error, entonces hagamos algo al respecto.

Creando la clase Registry

La clase Registry es utilizada para pasar datos globales entre objetos individuales, y actualmente es una clase realmente simple, y no necesitará mas que tres pequeños métodos

Primero, creamos un nuevo directorio llamado ‘classes’, y luego creamos un nuevo archivo llamado ‘registry.php’. Colocamos el siguiente código en el archive registry.php:

  1. <?php
  2. Class Registry {
  3. private $vars = array();
  4. }
  5. ?>

Nosotros tenemos ahora el esqueleto para la clase Registry, y todo lo que nosotros necesitamos es añadir algunos métodos. Todo lo que necesita la clase Registry es un método set(), para almacenar los datos, y un método get(), para recuperar los datos. Opcionalmente nosotros podemos también tener métodos para agregar o remover algún dato. Los siguientes tres métodos deben ser agregados a la clase Registry:

  1. function set($key, $var) {
  2. if (isset($this->vars[$key]) == true) {
  3. throw new Exception('Unable to set var `' . $key . '`. Already set.');
  4. }
  5. $this->vars[$key] = $var;
  6. return true;
  7. }
  8. function get($key) {
  9. if (isset($this->vars[$key]) == false) {
  10. return null;
  11. }
  12. return $this->vars[$key];
  13. }
  14. function remove($var) {
  15. unset($this->vars[$key]);
  16. }

Como tu puedes ver, estos tres métodos son realmente básicos, y todo lo que ellos realizan es almacenar, obtener y eliminar items de la propiedad $vars. En el método set() nosotros también chequeamos si los datos con una clave particular ya existe, nosotros lanzamos una excepción. Esto es una manera de proteger de la sobreescritura de datos de forma accidental.

Ahora tenemos completamente funcional la clase Registry, pero nosotros no pararemos aquí. Vamos a utilizar una de las características de SPL: ArrayAccess. SPL, que es el acrónimo de Standard PHP Library, es una colección de interfaces y clases que facilitan la solución de problemas comunes. Una de las interfaces de SPL, ArrayAccess, puede ser utilizada para proporcionar a un objeto el acceso a sus atributos como si fuese un arreglo. Considere el siguiente código:

  1. function offsetExists($offset) {
  2. return isset($this->vars[$offset]);
  3. }
  4. function offsetGet($offset) {
  5. return $this->get($offset);
  6. }
  7. function offsetSet($offset, $value) {
  8. $this->set($offset, $value);
  9. }
  10. function offsetUnset($offset) {
  11. unset($this->vars[$offset]);
  12. }

El acceso como arreglo hace que parezca que $registry es un arreglo, sin mostrarlo como un objeto. Aunque ArrayAccess no tiene ventajas reales, esto significa menos tipeo de código debido a que usted no tendrá que utilizar continuamente ->get(). Para utlizar ArrayAccess usted primero tendrá que cambiar la primera línea de la clase Registry, para que empiece con:

Registry Implements ArrayAccess {

La palabra reservada Implements es utilizada para implementar una iterface

Por la implementación de la interfase ArrayAccess, la clase debe también agregar cuatro nuevos métodos:

  1. function offsetExists($offset) {
  2. return isset($this->vars[$offset]);
  3. }
  4. function offsetGet($offset) {
  5. return $this->get($offset);
  6. }
  7. function offsetSet($offset, $value) {
  8. $this->set($offset, $value);
  9. }
  10. function offsetUnset($offset) {
  11. unset($this->vars[$offset]);
  12. }

Estos métodos se explican por si mismos, mayor información puede ser encontrada en la documentación de SPL.

Ahora tenemos implementada el ArrayAccess y podemos utilizar como un arreglo, como se pudo apreciar en el ejemplo anterior y el siguiente ejemplo:

  1. <?php
  2. $registry = new Registry;
  3. // Set some data
  4. $registry->['name'] = 'Dennis Pallett';
  5. // Get data, using get()
  6. echo $registry->get ('name');
  7. // Get data, using array access
  8. echo $registry['name']
  9. ?>

Nuestra clase Registry ahora esta terminada, y si usted intenta ejecutar el sistema todo debería funcionar (aunque nada se mostrará aun). El archivo de inicialización esta finalizado, y podemos continuar con el siguiente paso para construir nuestro sistema MVC: configurar la funcionalidad de base de datos, también llamado “Modelo”.

Sigue la parte III

Feb 18

El siguiente post corresponde a una traducción del articulo Building a simple MVC system with PHP5 por lo que el crédito lo dejo a sus respectivos autores. Únicamente me parece buena idea incrementar la información en español existente en Internet sobre programación y diseño en PHP en aspectos diferentes al estilo de programación estructurada.

Resumen

En este tutorial usted va a aprender como construir un sistema Modelo-Vista-Controlador simple con PHP 5.1 y algunas de las características de la Biblioteca Estándar de PHP (SPL’s).

Introducción

Bienvenido a este tutorial, usted necesitará tener PHP 5.1y SPL instalado para seguir este tutorial, esto debido a que utilizaremos varias de las ultimas características existentes en PHP5.
En este tutorial voy a mostrarles como construir un sistema Modelo-Vista-Controlador simple, el cual es el patrón de diseño más común para el desarrollo de grandes aplicaciones Web. Voy a guiarle por todos los pasos necesarios para iniciarte en el mundo de un verdadero sistema MVC

Un solo punto de entrada

Una aspecto importante relacionado con nuestro sistema MVC es que éste debe tener un único punto de entrada. Envés de tener varias docenas de archivos PHP que realicen lo siguiente:


include ('global.php');
// Aquí el código de la pagina actual
?>

Tendremos una sola página que maneje todas las peticiones. Esto significa que no tendremos que incluir el archivo global.php cada vez que deseamos crear una página nueva. Este “único punto de entrada” será llamado “index.php” y, hasta el momento, será algo así:

// Hace alguna cosa
?>

Como se puede apreciar, la página principal no hace nada todavía, pero lo hará en un minuto.
Para cerciorarse de que todas las peticiones vayan a la página principal configuraremos RewriteRule en el archivo .htaccess utilizando para ello el modulo de apache mod_rewrite. Debemos colocar el archivo llamado “.htaccess” en el mismo directorio que se encuentra el archivo de index.php y debe incluir el siguiente código:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?route=$1 [L,QSA]

Primero comprobamos si existe el archivo actual con el comando RewriteCond, y si no existe, lo redireccionamos al archivo index.php. Tenemos que comprobar si el archivo existe, porque también deseamos poder utilizar archivos normales que no son PHP, tales como imágenes JPEG.

Si usted no puede utilizar .htaccess o el modulo mod_rewrite, debería realizar las redirecciones manualmente a través de peticiones al archive index.php, lo cual significa que sus enlaces deben seguir el formato “index.php?route=[petición-va-aquí]“, ejemplo: index.php?route=chat/index.

Ahora que todas las peticiones están pasando a través de un solo punto de entrada, podemos comenzar a escribir el archivo index.php. Las primeras cosas que tendremos que realizar son algunas tareas inicialización. Para ello creamos un nuevo directorio llamado “includes”, y en este directorio creamos un nuevo archivo llamado “startup.php”. Luego debemos poner el siguiente código en el archivo index.php:

# Tareas de inicialización (definición de constantes, etc.)
require 'includes/startup.php';
?>

Continua con la II Parte

Feb 12

Empezando con la promesa de publicar una serie de post sobre patrones y antipatrones he decidido iniciar con “Reinventar la rueda” o en ingles “Reinvent the Wheel”. Para empezar se debe especificar que se trata de un antipatron metodológico que básicamente consiste en ignorar una solución probada y garantizada para desarrollar una solución propia con el fin de resolver un problema dado y que a la final resulta en la misma solución. Resumidamente es el “Esfuerzo ya realizado por otros pero desconocido para nosotros”

Este problema suele ser muy común en compañías y personas donde se empleada demasiado tiempo al desarrollar toda una solución desde cero, es un problema que encaja en lo metodológico, en el diseño y al momento de programar. Por ello si no se tiene mucha experiencia en un problema siempre es recomendable tomarse un tiempo para investigar posibles soluciones y de ser posible revisar el código para ver la forma de solucionarlo. Sin embargo en ocasiones no siempre es bueno utilizar el mismo código de otra solución para ofrecerlo en nuestros proyectos o negocios básicamente por que esto puede ser ocasionar un serio problema de seguridad.

Ahora si lo tomamos desde otro punto de vista, el reinventar la rueda no siempre es malo pues en el caso de estudiantes puede ser una buena estrategia para entender problemas complejos o perfeccionar la destreza de diseño. Otra justificación para reinventar la rueda puede ser el simple echo que no estemos conforme con las soluciones existentes, claro que siempre nos servirán de guía para crear nuestra solución.

Si tomamos en cuenta este significado dual que se le puede dar a este antipatron no queda mas que saber discernir que hacer tomando en cuenta muchos mas aspectos que aquí no se nombran, como los derechos de autor que pueden ser influyentes.

Finalmente y como recomendación yo siempre creo que es bueno (siempre que se pueda) agregar alguna mejora a la solución existente pues esto nos dará un valor agregado ante la competencia.

Feb 04

Antes de iniciar una serie de post sobre los patrones y antipatrones de diseño, vale la pena aclarar que son los Patrones y Antipatrones de diseño.

Básicamente los patrones de diseño son soluciones a problemas de diseño no trivial que se producen comúnmente en temas relacionados con el desarrollo de software, algunos ejemplos pueden ser los patrones creacionales y de comportamiento. Por otro lado un antipatron es un patron de diseño que llevará a una mala solución de un problema, en realidad es lo que no deberíamos hacer o lo que debemos tratar de evitar hacer siempre que sea posible….

Debido a que ultimamente me encuentro trabajando bastante sobre PHP trataré en lo posible de aplicar los conceptos expuestos a este lenguaje de programación (especialmente en POO)


Switch to our mobile site