PHP orientado a objetos 10: mvc

Cómo montar una estructura web en PHP siguiendo el patrón MVC

archivado en: PHP/AJAX / 2 noviembre, 2014 / taller:

Retomo esta serie de php orientado a objetos que tenía descuidada por razones que no vienen al caso para hablar del patrón modelo - vista - controlador aka mvc. Tomo como referencia para este post un artículo muy bueno de Anant Garg titulado Write your own PHP MVC Framework.

El patrón mvc no es propio de PHP, aunque es perfecto para lo que se suele hacer con este lenguaje, y lo que viene a proponer es que el código de una aplicación, como el de una web, se debe agrupar en tres grandes partes: Una denominada modelo es la encargada de gestionar la relación con la base de datos; otra son las vistas, esto es, la parte que ve el cliente; y la tercera son los controladores, el código encargado de organizar el tráfico entre las vistas y los modelos. Por retomar un símil que he mencionado alguna vez, es como si en un restaurante las vistas fueran los clientes, los controladores los camareros y los modelos los cocineros.

En general, si tenemos que programar una web en php que siga un patrón mvc usaremos un framework ex profeso, como Codeigniter o, mejor aún, Simfony, pero está bien que entendamos cómo funciona en esencia a partir de nuestro propio frame tiny-mvc : ).

Una de las claves fundamentales es establecer una sola puerta de entrada, es decir, que todo el tráfico hacia una web se redirija a un solo sitio, desde donde cargaremos los distintos controladores. Y para eso nada mejor que escribir unas reglas en el archivo .haccess a partir de una estructura similar a esta:

application

controllers

models

views

config

libraries

public

css

fonts

img

js

En el directorio raíz ponemos estas reglas, que harán que todo se dirija al directorio public. Además, podemos añadir otra para forzar conexiones utf-8.

<IfModule mod_rewrite.c>

RewriteEngine on

RewriteRule ^$ public/ [L]

RewriteRule (.*) public/$1 [L]

</IfModule>

<IfModule mod_mime.c>

AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml

</IfModule>

Luego, en el directorio public podemos poner algo así para que sea cual sea la dirección que se escriba en el navegador se cargue el archivo index.php, que también pondremos en la raíz de public.

<IfModule mod_rewrite.c>

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*)$ index.php?url=$1 [PT,L]

</IfModule>

En este archivo index.php podemos definir una montonera de cosas, pero cuanto menos debería tener algo así:

<?php

/* En modo desarrollo activamos todos los errores */

error_reporting(E_ALL);

/* Definimos dos constantes, una opcional DS, con el tipo de barra que usa el servidor, y otra, ROOT, con la ruta base */

define('DS', DIRECTORY_SEPARATOR);

define('ROOT', dirname(dirname(__FILE__)));

/* Indicamos una url por defecto si no se especifica ningún path */

if (isset($_GET['url']) ) {

$url = rtrim($_GET['url']);

} else {

$url = "main/index";

}

/* Cargamos el archivo config, con las especificaciones que tenga, como la conexión a la base de datos, y el archivo init, donde cargaremos un controlador u otro en función de la ruta */

require_once (ROOT . DS . 'config' . DS . 'config.php');

require_once (ROOT . DS . 'libraries' . DS . 'init.php');

En el archivo config.php, que guardaremos en la carpeta config, definimos cuanto menos la conexión con la base de datos, aunque aquí podemos definir otras constantes que necesitemos.

<?php

define('DB_USER', 'root');

define('DB_PASS', '');

define('DB_HOST', 'localhost');

define('DB_NAME', 'nombre de la tabla');

En init.php, que guardaremos en libraries, está la otra clave del asunto: un sistema de rutas basado en la función call_user_func_array(), que permite invocar una función pasándole un array de parámetros. Así, lo único que tenemos que hacer es descomponer la url y —sabiendo que en la primera posición estará el controlador, en la segunda la función de ese controlador y a partir de ahí el resto de parámetros que espera esa función— utilizar un call_user_func_array.

<?php

/* Una página de error personalizada */

function errorPage() {

require_once( ROOT . DS . 'application/views/404.html');

die();

}

function routes($controllersApplication) {

global $url;

$urlArray = array();

$urlArray = explode("/",$url);

if ( in_array($urlArray[0], $controllersApplication) ) {

$controller = $urlArray[0];

array_shift($urlArray);

$action = $urlArray[0];

array_shift($urlArray);

$queryString = $urlArray;

$controllerName = $controller;

$controller = ucwords($controller);

$dispatch = new $controller($controllerName,$action);

if ((int)method_exists($controller, $action)) {

call_user_func_array(array($dispatch,$action),$queryString);

} else {

errorPage();

}

} else {

errorPage();

}

}

function loadClass() {

require_once ('../application/models/modelo.php');

require_once ('../application/controllers/main.php');

}

/* Añadir aquí los nuevos controladores que se vayan generando */

$controllersApplication = ['main'];

loadClass();

routes($controllersApplication);

Y con esto ya estaría hecho lo más complicado. Luego o bien instanciando una clase modelo y otra controlador si el proyecto es complejo o bien usando directamente una clase padre para cada una de estas funciones, podemos hacer algo así. En el controlador:

<?php class Main {

protected $modelo;

public function __construct () {

$this->modelo = new Modelo();

}

public function index() {

$todoAlgo = $this->modelo->getAlgo();

require_once( ROOT . '/application/views/commons/header.php' );

require_once( ROOT . '/application/views/index.php' );

require_once( ROOT . '/application/views/commons/footer.php' );

}

/* Para llamadas ajax */

public function otraFuncion() {

$todoOtroAlgo = json_encode( $this->modelo->getOtroAlgo() );

echo $todoOtroAlgo;

}

} /* #Main */

Y en el modelo, devolvemos los resultados como un array.

<?php class Modelo {

private $conexionDB;

public function __construct () {

$this->conexionDB = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);

$this->conexionDB->query("set character_set_results='utf8'");

if ( $this->conexionDB->connect_error ) {

echo "Error de conexión con la base de datos, por favor, revisen los datos de configuración";

exit;

}

}

public function getAlgo() {

$arrayReturn = [];

$result = $this->conexionDB->query("SELECT * FROM `algo`");

while ( $filas=$result->fetch_assoc() ) {

$arrayReturn[] = $filas;

}

return $arrayReturn;

} /* #getAlgo*/

} /* #Modelo */

De tal manera, que solo tengamos que recorrerlo en las vistas que hemos cargado con el require en el controlador.

<?php foreach ( $todoAlgo as $clave ) { ?>

<?= $clave['valor']; ?>

<?php } ?>

Bueno, no he explicado en detalle todo, ya que vuelvo a estar sin una miserable migaja de tiempo, pero espero que lo expuesto sirva de punto de partida para quien lo necesite.

descargar código desde git-hub

|| Tags: ,

valoración de los lectores sobre PHP orientado a objetos 10: mvc

  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración negativa
  • 4.4 sobre 5 (5 votos)

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

2 respuestas a “PHP orientado a objetos 10: mvc

  1. carlos el dijo:

    Seguiras con los tutoriales?
    yo por ejemplo para conectar a una base de datos puedo usar solo mysqli orientado a objetos o tendria que crear una nueva conexion mysqli orientado a objetos?? muchas gracias

  2. marcos el dijo:

    Hola Carlos, gracias. Bien hecho así.

    No tengo claro cuándo retomaré esta serie, antes tengo que terminar la de node, mongo y una de backbone… : (, lo siento, pero mi tiempo no da más de sí.