24 julio 2016

El Patrón Modelo-Vista-Controlador y Programación Orientada a Objetos en PHP Profesional

  4 comentarios

Para los que constantemente han visitado este blog entrada tras entrada sabrán del gran esfuerzo que hago por volver aquí, quizás no con tanta frecuencia como quisiera porque entre el trabajo y los estudios, no me queda mucho tiempo al día, mas sin embargo, no me olvido de lo que me gusta hacer realmente y de compartir mis conocimientos, y todo lo que por este medio se pueda expresar para los seguidores de Friki.

Disculpen lo confianzudo, puesto a que sigo otros blog de la misma temática que el mio y son un poco mas técnicos y mas al punto de lo que desean exponer, mas sin embargo, a mi persona, le aburre ser técnico y todo cuadriculado, y solo espero crear un 'clima' de confianza usuario-administrador si se pudiera llamar de alguna manera con el fin de que al entrar al blog se sientan motivados a seguir aprendiendo de una manera como tal, explicativa, comprensible usando el tipico metodo de explicar las cosas con manzanas y peras


Continuando con lo que vengo a exponer hoy, que ya veniamos hablando en anteriores entradas las cuales invito a ver, para que tengan una idea de lo que va a tratar este tutorial


-Programacion Orientada a Objetos y la estructura Modelo-Vista-Controlador en PHP Básico - Introducción
-Programacion Orientada a Objetos y la estructura Modelo-Vista-Controlador en PHP Básico - Programando

No hace falta adentrarnos mas en explicaciones de lo que es el patrón MVC y la POO en PHP, así que vamos al grano, en los tutoriales anteriores explique los mismos, y realice un ejercicio práctico más sin embargo, fue algo sumamente básico y que aun contiene ciertos elementos que pueden ser mejorados separando aun mas la lógica del negocio de la interfaz, y optimizando el uso de los códigos de cada lenguaje de programación, el tutorial a continuación, representa la programación en capas con MVC y POO en PHP.

Como ya sabemos, el patrón MVC (Modelo-Vista-Controlador) es una arquitectura de software, un modo de programar cualquier tipo de programa y páginas web que separa la lógica del negocio de la interfaz del usuario, cumpliendo cada capa una función específica

- Modelo: representa la lógica de negocios. Es el encargado de acceder de forma directa a los datos actuando como “intermediario” con la base de datos.

- Vista: es la encargada de mostrar la información al usuario de forma gráfica y “humanamente legible”.

- Controlador: es el intermediario entre la vista y el modelo. Es quien controla las interacciones del usuario solicitando los datos al modelo y entregándolos a la vista para que ésta, lo presente al usuario, de forma “humanamente legible”.

¿Cómo funciona el MVC?



El funcionamiento básico del patrón MVC, puede resumirse en:

• El usuario realiza una petición
• El controlador captura el evento (puede hacerlo mediante un manejador de eventos – handler -, por ejemplo)
• Hace la llamada al modelo/modelos correspondientes (por ejemplo, mediante una llamada de retorno – callback -) efectuando las modificaciones pertinentes sobre el modelo
• El modelo será el encargado de interactuar con la base de datos, ya sea en forma directa, con una capa de abstracción para ello, un Web Service, etc. y retornará esta información al controlador
• El controlador recibe la información y la envía a la vista
• La vista, procesa esta información pudiendo hacerlo desde el enfoque que veremos en este libro, creando una capa de abstracción para la lógica (quien se encargará de procesar los datos) y otra para el diseño de la interfaz gráfica o GUI. La lógica de la vista, una vez procesados los datos, los “acomodará” en base al diseño de la GUI - layout – y los entregará al usuario de forma “humanamente legible”.

Ahora que ya sabemos esto, es el momento de programar. Se creara un registro de usuarios que contara con el registro de datos así como la modificación, consulta y eliminacion del usuario, en pocas palabras, la gestión de usuario.

Para comenzar, vamos a crear en nuestro servidor, ya sea local o externo, una carpeta llamada 'mvc' en el cual, crearemos los siguientes directorios

core: el propósito de este directorio, será almacenar todas aquellas clases, helpers, utils, decorators, etc, que puedan ser reutilizados en otros módulos de la aplicación (aquí solo crearemos el de usuarios), es decir, todo aquello que pueda ser considerado “núcleo” del sistema.

site_media: será el directorio exclusivo para nuestros diseñadores. En él, organizadamente, nuestros diseñadores podrán trabajar libremente, creando los archivos estáticos que la aplicación requiera.

usuarios: este, será el directorio de nuestro módulo de usuarios. En él, almacenaremos los archivos correspondientes al modelo, la lógica de la vista y el controlador, exclusivos del módulo de usuarios.

El Modelo


Archivo ./core/db_abstract_model.php

Este archivo contiene todos los métodos de consulta los cuales nos permitirán conectarnos con la base de datos y extraer los datos, valga la redundancia que necesitemos según nuestra consulta SQL

<?php
abstract class DBAbstractModel {
  private static $db_host = 'localhost';
  private static $db_user = 'root';
  private static $db_pass = 'root';
  protected $db_name = 'mvc';
  protected $query;
  protected $rows = array();
  private $conn;
  public $mensaje = 'Hecho';

  # métodos abstractos para ABM de clases que hereden
  abstract protected function get();
  abstract protected function set();
  abstract protected function edit();
  abstract protected function delete();

  # los siguientes métodos pueden definirse con exactitud y
  # no son abstractos

  # Conectar a la base de datos
 private function open_connection() {
   $this->conn = new mysqli(self::$db_host, self::$db_user,
   self::$db_pass, $this->db_name);
 }

 # Desconectar la base de datos
 private function close_connection() {
  $this->conn->close();
 }

 # Ejecutar un query simple del tipo INSERT, DELETE, UPDATE
 protected function execute_single_query() {
   if($_POST) {
    $this->open_connection();
    $this->conn->query($this->query);
    $this->close_connection();
   } else {
    $this->mensaje = 'Metodo no permitido';
   }
 }

 # Traer resultados de una consulta en un Array
 protected function get_results_from_query() {
   $this->open_connection();
   $result = $this->conn->query($this->query);
   while ($this->rows[] = $result->fetch_assoc());
   $result->close();
   $this->close_connection();
   array_pop($this->rows);
 }
}
?>


Archivo ./usuarios/model.php

Este archivo representa la clase de la entidad usuario, contiene todos los métodos y propiedades del usuario así como las consultas SQL que nos servirán para gestionar sus datos.

<?php
# Importar modelo de abstracción de base de datos
require_once('../core/db_abstract_model.php');
class Usuario extends DBAbstractModel {
############################### PROPIEDADES ################################
 public $nombre;
 public $apellido;
 public $email;
 private $clave;
 protected $id;

################################# MÉTODOS ##################################
 # Traer datos de un usuario
 public function get($user_email='') {
  if($user_email != '') {
   $this->query = "
   SELECT id, nombre, apellido, email, clave
   FROM usuarios
   WHERE email = '$user_email'
   ";
   $this->get_results_from_query();
  }
  if(count($this->rows) == 1) {
   foreach ($this->rows[0] as $propiedad=>$valor) {
    $this->$propiedad = $valor;
   }
   $this->mensaje = 'Usuario encontrado';
  } else {
   $this->mensaje = 'Usuario no encontrado';
  }
 }

 # Crear un nuevo usuario
 public function set($user_data=array()) {
  if(array_key_exists('email', $user_data)) {

   $this->get($user_data['email']);

   if($user_data['email'] != $this->email) {
    foreach ($user_data as $campo=>$valor) {
     $$campo = $valor;
     }

   $this->query = "
    INSERT INTO usuarios
    (nombre, apellido, email, clave)
    VALUES
    ('$nombre', '$apellido', '$email', '$clave')
    ";
    $this->execute_single_query();
    $this->mensaje = 'Usuario agregado exitosamente';
   } else {
    $this->mensaje = 'El usuario ya existe';
   }
  } else {
   $this->mensaje = 'No se ha agregado al usuario';
  }
 }


 # Modificar un usuario
 public function edit($user_data=array()) {
  foreach ($user_data as $campo=>$valor) {
   $$campo = $valor;
  }

  $this->query = "
  UPDATE usuarios
  SET nombre='$nombre',
  apellido='$apellido'
  WHERE email = '$email'
  ";
  $this->execute_single_query();
  $this->mensaje = 'Usuario modificado';
 }


 # Eliminar un usuario
 public function delete($user_email='') {
  $this->query = "
  DELETE FROM usuarios
  WHERE email = '$user_email'
  ";
  $this->execute_single_query();
  $this->mensaje = 'Usuario eliminado';
 }

 # Método constructor
 function __construct() {
  $this->db_name = 'mvc';
 }

 # Método destructor del objeto
 function __destruct() {
  unset($this);
 }
}
?>


La Vista

Archivo ./site_media/html/usuario_template.html

Esta representa la plantilla HTML general donde se mostraran cada uno de los formularios solicitados por el usuario así como mensajes de confirmacion, esta sera la interfaz mostrada al usuario, lo unico con lo que el usuario podrá interactuar en este caso.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="es">

 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <link rel="stylesheet" type="text/css" href="/mvc/site_media/css/base_template.css">
  <title>ABM de Usuarios: {subtitulo}</title>
 </head>

 <body>

  <div id="cabecera">
    <h1>Administrador de usuarios</h1>
    <h2>{subtitulo}</h2>
  </div>

  <div id="menu">
    <a href="/mvc/{VIEW_SET_USER}" title="Nuevo usuario">Agregar usuario</a>
    <a href="/mvc/{VIEW_GET_USER}" title="Buscar usuario">Buscar/editar usuario</a>
    <a href="/mvc/{VIEW_DELETE_USER}" title="Borrar usuario">Borrar usuario</a>
  </div>

  <div id="mensaje">
    {mensaje}
  </div>

  <div id="formulario">
   {formulario}
  </div>
  
 </body>
</html>


Archivo ./site_media/html/usuario_agregar.html

Este y los siguientes 3 archivos corresponden a los formularios que solicitara el usuario para realizar una función cualquiera, estos formularios serán mostrados en la plantilla general sustituyendo la linea {formulario} por el código presente en el archivo según sea el caso

<form id="alta_usuario" action="{SET}" method="POST">

  <div class="item_requerid">E-mail</div>
  <div class="form_requerid"><input type="text" name="email" id="email"></div>

  <div class="item_requerid">Clave</div>
  <div class="form_requerid"><input type="password" name="clave" id="clave"></div>

  <div class="item_requerid">Nombre</div>
  <div class="form_requerid"><input type="text" name="nombre" id="nombre"></div>

  <div class="item_requerid">Apellido</div>
  <div class="form_requerid"><input type="text" name="apellido" id="apellido"></div>
  
  <div class="form_button"><input type="submit" name="enviar" id="enviar" value="Agregar"></div>
  
</form>


Archivo ./site_media/html/usuario_borrar.html
(formulario para eliminar un usuario identificado por su e-mail)

<form id="alta_usuario" action="{DELETE}" method="POST">

  <div class="item_requerid">E-mail</div>

  <div class="form_requerid"><input type="text" name="email" id="email"></div>

  <div class="form_button"><input type="submit" name="enviar" id="enviar" value="Eliminar"></div>

</form>


Archivo ./site_media/html/usuario_buscar.html
(archivo que permite buscar un usuario por su e-mail para luego mostrarlo en el formulario de edición, ya sea para editarlo o solo para ver sus datos)

<form id="alta_usuario" action="{GET}" method="GET">

  <div class="item_requerid">E-mail</div>
  <div class="form_requerid"><input type="text" name="email" id="email"></div>
 
  <div class="form_button"><input type="submit" id="enviar" value="Buscar"></div>
  
</form>


Archivo ./site_media/html/usuario_modificar.html
(archivo para visualizar los datos de un usuario y modificarlos)

<form id="alta_usuario" action="{EDIT}" method="POST">

  <div class="item_requerid">E-mail</div>
  <div class="form_requerid"><input type="text" name="email" id="email" value="{email}" readonly></div>
  
  <div class="item_requerid">Nombre</div>
  <div class="form_requerid"><input type="text" name="nombre" id="nombre" value="{nombre}"></div>
  
  <div class="item_requerid">Apellido</div>
  <div class="form_requerid"><input type="text" name="apellido" id="apellido" value="{apellido}"></div>
  
  <div class="form_button"><input type="submit" name="enviar" id="enviar" value="Guardar"></div>

</form>


Archivo ./site_media/css/base_template.css
(hoja de estilos en cascada)

body {
 margin: 0px 0px 0px 0px;
 background-color: #ffffff;
 color: #666666;
 font-family: sans-serif;
 font-size: 12px;

}
#cabecera {
 padding: 6px 6px 8px 6px;
 background-color: #6699FF;
 color: #ffffff;
}
#cabecera h1, h2 {
 margin: 0px 0px 0px 0px;
}
#menu {
 background-color: #000000;
 color: #ffffff;
 padding: 4px 0px 4px 0px;
}
#menu a {
 width: 100px;
 background-color: #000000;
 padding: 4px 8px 4px 8px;
 color: #f4f4f4;
 text-decoration: none;
 font-size: 13px;
 font-weight: bold;
}
#menu a:hover {
 background-color: #6699FF;
 color: #ffffff;
}
#mensaje {
 margin: 20px;
 border: 1px solid #990033;
 background-color: #f4f4f4;
 padding: 8px;
 color: #990033;
 font-size: 15px;
 font-weight: bold;
 text-align: justify;
}
#formulario {
 margin: 0px 20px 10px 20px;
 border: 1px solid #c0c0c0;
 background-color: #f9f9f9;
 padding: 8px;
 text-align: left;
}
.item_requerid {
 width: 150px;
 height: 22px;
 padding-right: 4px;
 font-weight: bold;
 float: left;
 clear: left;
 text-align: right;
}

.form_requerid {
 height: 22px;
 float: left;
 clear: right;
}
input {
 border: 1px solid #c0c0c0;
}
.form_button {
 padding-top: 15px;
 margin-left: 154px;
 clear: both;
}
#enviar {
 padding: 5px 10px 5px 10px;
 border: 2px solid #ffffff;
 background-color: #000000;
 color: #ffffff;
 font-family: sans-serif;
 font-size: 14px;
 font-weight: bold;
 cursor: pointer;
}
#enviar:hover {
 background-color: #6699FF;
}

Archivo ./usuarios/view.php
(Lógica de la vista)

<?php
$diccionario = array(

 'subtitle'=>array(
  VIEW_SET_USER=>'Crear un nuevo usuario',
  VIEW_GET_USER=>'Buscar usuario',
  VIEW_DELETE_USER=>'Eliminar un usuario',
  VIEW_EDIT_USER=>'Modificar usuario'
 ),

 'links_menu'=>array(
  'VIEW_SET_USER'=>MODULO.VIEW_SET_USER.'/',
  'VIEW_GET_USER'=>MODULO.VIEW_GET_USER.'/',
  'VIEW_EDIT_USER'=>MODULO.VIEW_EDIT_USER.'/',
  'VIEW_DELETE_USER'=>MODULO.VIEW_DELETE_USER.'/'
 ),

 'form_actions'=>array(
  'SET'=>'/mvc/'.MODULO.SET_USER.'/',
  'GET'=>'/mvc/'.MODULO.GET_USER.'/',
  'DELETE'=>'/mvc/'.MODULO.DELETE_USER.'/',
  'EDIT'=>'/mvc/'.MODULO.EDIT_USER.'/'
 )
);

function get_template($form='get') {
 $file = '../site_media/html/usuario_'.$form.'.html';
  $template = file_get_contents($file);
  return $template;
}

function render_dinamic_data($html, $data) {
  foreach ($data as $clave=>$valor) {
   $html = str_replace('{'.$clave.'}', $valor, $html);
  }
  return $html;
}

function retornar_vista($vista, $data=array()) {
 global $diccionario;
 $html = get_template('template');
 $html = str_replace('{subtitulo}', $diccionario['subtitle'][$vista],
 $html);
 $html = str_replace('{formulario}', get_template($vista), $html);
 $html = render_dinamic_data($html, $diccionario['form_actions']);
 $html = render_dinamic_data($html, $diccionario['links_menu']);
 $html = render_dinamic_data($html, $data);
 // render {mensaje}
 if(array_key_exists('nombre', $data) && array_key_exists('apellido', $data) && $vista==VIEW_EDIT_USER) {
  $mensaje = 'Editar usuario '.$data['nombre'].' '.$data['apellido'];
 } else {
  if(array_key_exists('mensaje', $data)) {
   $mensaje = $data['mensaje'];
  } else {
   $mensaje = 'Datos del usuario:';
  }
 }
 $html = str_replace('{mensaje}', $mensaje, $html);
 print $html;
}
?>

El Controlador


Archivo ./usuarios/controller.php


<?php
require_once('constants.php');
require_once('model.php');
require_once('view.php');


function handler() {
  $event = VIEW_GET_USER;
  $uri = $_SERVER['REQUEST_URI'];

  $peticiones = array(SET_USER, GET_USER, DELETE_USER, EDIT_USER,VIEW_SET_USER, VIEW_GET_USER, VIEW_DELETE_USER,VIEW_EDIT_USER);

  foreach ($peticiones as $peticion) {
   $uri_peticion = MODULO.$peticion.'/';

   if(strpos($uri, $uri_peticion) == true ) {
    $event = $peticion;
   }
  }

  $user_data = helper_user_data();

  $usuario = set_obj();

  switch ($event) {
   case SET_USER:
    $usuario->set($user_data);
    $data = array('mensaje'=>$usuario->mensaje);
    retornar_vista(VIEW_SET_USER, $data);
   break;
   case GET_USER:
    $usuario->get($user_data);
    $data = array(
     'nombre'=>$usuario->nombre,
     'apellido'=>$usuario->apellido,
     'email'=>$usuario->email
    );
    retornar_vista(VIEW_EDIT_USER, $data);
   break;
   case DELETE_USER:
    $usuario->delete($user_data['email']);
    $data = array('mensaje'=>$usuario->mensaje);
    retornar_vista(VIEW_DELETE_USER, $data);
   break;
   case EDIT_USER:
    $usuario->edit($user_data);
    $data = array('mensaje'=>$usuario->mensaje);
    retornar_vista(VIEW_GET_USER, $data);
   break;
   default:
    retornar_vista($event);
  }
}


function set_obj() {
  $obj = new Usuario();
  return $obj;
}


function helper_user_data() {
  $user_data = array();
  if($_POST) {
   if(array_key_exists('nombre', $_POST)) {
    $user_data['nombre'] = $_POST['nombre'];
   }
   if(array_key_exists('apellido', $_POST)) {
    $user_data['apellido'] = $_POST['apellido'];
   }
   if(array_key_exists('email', $_POST)) {
    $user_data['email'] = $_POST['email'];
   }
   if(array_key_exists('clave', $_POST)) {
    $user_data['clave'] = $_POST['clave'];
   }
  } else if($_GET) {
   if(array_key_exists('email', $_GET)) {
    $user_data = $_GET['email'];
   }

  }
  return $user_data;
}

handler();

?>

Archivo ./usuarios/constants.php

Este archivo contiene todas las constantes del módulo y no se le debería agregar ningún dato que no fuese una constante.

<?php
const MODULO = 'usuarios/';

# controladores
const SET_USER = 'set';
const GET_USER = 'get';
const DELETE_USER = 'delete';
const EDIT_USER = 'edit';

# vistas
const VIEW_SET_USER = 'agregar';
const VIEW_GET_USER = 'buscar';
const VIEW_DELETE_USER = 'borrar';
const VIEW_EDIT_USER = 'modificar';
?>

Archivo Adiciones

Script SQL

En el DBMS phpMyAdmin o cualquier otro, vamos a crear una base de datos cuyo nombre será 'mvc' en la cual crearemos la siguiente tabla

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";

CREATE TABLE IF NOT EXISTS `usuarios` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nombre` varchar(20) NOT NULL,
  `apellido` varchar(20) NOT NULL,
  `email` varchar(50) NOT NULL,
  `clave` varchar(8) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;


Archivo ./.htaccess

 Este archivo debe estar ubicado en la raíz del sistema, es decir, dentro de la carpeta 'mvc' directamente, vas a crear dicho archivo el cual contendrá la siguiente lineas

RewriteEngine on
RewriteRule ^usuarios/ usuarios/controller.php

Es importante tener el mod_rewrite de apache activo para que pueda funcionar el archivo .htaccess y las direcciones URL del sistema como tal, pues, si este no esta activo, no va a funcionar correctamente el flujo de los archivos ni de la información

Aqui dejo un tutorial bastante bueno en tal caso de que no lo tengas activo

Activar módulo mod_rewrite de Apache en Linux y Windows

Este tema aun tiene mucha tela que cortar, mas sin embargo, me limitare a responder solamente las dudas en los comentarios con el fin de no hacer este tutorial mas largo. A programar chicos!

 ref: Bahit, Eugenia. MVC y POO en PHP.

Enlaces para compartir en tu blog o pagina web.




Widget por Friki Bloggeo


4 comentarios :

  1. It’s quite difficult for the users to tackle the situation when their account gets hacked by any professional hacker. Have you ever been into the situation when you experience hacking incident? Well, despite the tight security, hackers due to their inventive tricks manage to steal the information in user’s account. To fix the hacking Blockchain Support NUmber situation, you can always feel free to call on Blockchain support number which is working 24/7 and one can ping on the number as per the need.

    ResponderEliminar
  2. It’s quite difficult for the users to tackle the situation when their account gets hacked by any professional hacker. Have you ever been into the situation when you experience hacking incident? Well, despite the tight security, hackers due to their inventive tricks Gemini Support Number manage to steal the information in user’s account. To fix the hacking situation, you can always feel free to call on Gemini support number which is working 24/7 and one can ping on the number as per the need.

    ResponderEliminar
  3. Facing non-working google authentication every now and then? Want to fix the error from the roots? In such case, dial Libra Coin support phone number and take assistance from the well-experienced experts who know every element of Libra Coin. The expert’s team will offer those solutions that are easy to implement and remember. You can reach out to the professionals by dialing the phone number or Libra Support Number dropping your queries on chat box or through an email. They always take user’s queries as their first priority and work from full-dedication.

    ResponderEliminar
  4. Are you unable to fix two-authentication in your Binance exchange? If you are surrounded by such issues and looking for the need of guidance, you can always have conversation with the elite members who are ready to guide you at every step. You can call them on Binance support number which is always functional and the team is ready to guide you at every step. Call them anytime for the result-driven remedies that are easy to execute and can be remember for the longer time. Myetherwallet support Number

    ResponderEliminar