Diferencia entre revisiones de «PHP Conceptos básicos Modelo-Vista-Controlador»
Ir a la navegación
Ir a la búsqueda

Imaxe obtida deste tutorial
| Línea 18: | Línea 18: | ||
| − | + | * '''Clase para acceder a Mysql''' | |
* En primeiro lugar, poderíamos crear unha clase que se encargue da facer a conexión á base de datos e as operacións contra a mesma. | * En primeiro lugar, poderíamos crear unha clase que se encargue da facer a conexión á base de datos e as operacións contra a mesma. | ||
| Línea 244: | Línea 244: | ||
* Tamén poderíamos aplicar os [http://wiki.cifprodolfoucha.es/index.php?title=PHP_Programaci%C3%B3n_Orientada_a_obxectos conceptos da POO que aprendemos anteriormente na wiki] e facer que a información que devolva a función 'consultarLibros' da clase Database, sexa un array de obxectos dunha clase definida previamente, que representaría a táboa LIBROS. | * Tamén poderíamos aplicar os [http://wiki.cifprodolfoucha.es/index.php?title=PHP_Programaci%C3%B3n_Orientada_a_obxectos conceptos da POO que aprendemos anteriormente na wiki] e facer que a información que devolva a función 'consultarLibros' da clase Database, sexa un array de obxectos dunha clase definida previamente, que representaría a táboa LIBROS. | ||
| − | |||
| − | |||
| − | |||
| − | + | * No seguinte punto imos aprender como 'separar' en diferentes 'capas' a aplicación para que o mantemento do proxecto sexa moito máis doado. | |
| − | |||
| − | * | ||
| − | |||
| − | ''' | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
== Modelo-Vista-Controlador == | == Modelo-Vista-Controlador == | ||
Revisión del 08:49 26 ene 2024
Sumario
Introdución
- Có que levamos visto ata o de agora xa podemos facer sitios webs en PHP, pero teremos certos problemas 'de mantemento' en caso de que o sitio sexa grande (con grande me refiro a unha cantidade alta de páxinas PHP).
- Isto é debido a que estamos a 'mesturar' aspectos, como por exemplo as funcións de acceso a datos (conexión e operacións) dentro das páxinas que van 'presentar' ao usuario a información.
- Imaxinemos como estamos a facer agora que queremos cambiar o acceso á base de datos e no canto de utilizar Mysql queremos usar SQLServer...
- Teríamos que ir a todas as páxinas cambiando as funcións de acceso a datos.
- Por outra banda, imaxinade que temos un equipo de persoas e queremos 'distribuír' o traballo en equipos.
- De tal forma que un equipo se encargue de 'maquetar' a información con CSS e HTML e outro que se adique a facer as chamadas á base de datos en busca de dita información.
- Esta labor non a poderíamos facer en 'paralelo' xa que a presentación de datos e o acceso aos mesmos se fan dentro da mesma páxina.
Melloras ao feito ata o de agora
- Clase para acceder a Mysql
- En primeiro lugar, poderíamos crear unha clase que se encargue da facer a conexión á base de datos e as operacións contra a mesma.
<?php class Database { const CONSULTAR_LIBROS='select id_libro,titulo from LIBROS order by titulo'; const SERVIDOR = 'localhost'; const USUARIO = 'user_php'; const PASSWORD = 'user_php'; const BD = 'PHP'; private static $db; private $conexion; private function __construct() { $this->conexion = new mysqli(self::SERVIDOR,self::USUARIO,self::PASSWORD,self::BD); $this->conexion->set_charset('utf8'); } function __destruct() { if(!$this->conexion) $this->conexion->close(); } public static function obterConexion() { if (self::$db == null) { self::$db = new Database(); } return self::$db->conexion; } public static function executarSQL($consulta) { $result=self::$db->conexion->query($consulta); return $result; } public static function executarProc($procedure) { if(self::$db->conexion->multi_query($procedure)){ $result[]=self::$db->conexion->store_result(); while(self::$db->conexion->next_result()){ $result[]= self::$db->conexion->store_result(); } return $result; } else{ return null; } } }
Nota: Lembrar que a clase anterior non tería que estar na estrutura de cartafol de acceso do Apache (en Linux sería /var/www/html/), como indicamos anteriormente na WIKI.
- Se aplicamos a clase anterior a unha das páxinas feitas durante este manual, por exemplo, a páxina que consulta os libros da táboa LIBROS:
<?php include ('Database.php'); $conex = Database::obterConexion(); if ($conex->connect_error){ die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); } ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Operacións BD</title> </head> <body> <?php $result=Database::executarSQL(Database::CONSULTAR_LIBROS); if ($result && $result->num_rows>0){ echo "<table border='1'>"; while($row = $result->fetch_assoc()) { echo '<tr>'; printf("<td>%s</td>",$row["titulo"]); echo '</tr>'; } echo '</table>'; $result->free(); // Liberamos da memoria os recursos } else { echo "Non hai datos que amosar!!!!"; } ?> </body> </html>
- Poderíamos 'optimizar' un pouco máis e facer que a clase DataBase tamén incorpore os métodos que respondan ás peticións das páxinas, facendo que os métodos definidos devolvan xa un array cos datos.
- Desta forma, nesta clase, poderíamos poñer as regras de validación en caso de engadir datos, xunto cos procedementos que devolven ou xestionan a información que reciben dende as páxinas (regras de negocio)
- Aplicado ao exemplo anterior:
Arquivo: Database.php
<?php class Database { const CONSULTAR_LIBROS='select id_libro,titulo from LIBROS order by titulo'; const SERVIDOR = 'localhost'; const USUARIO = 'user_php'; const PASSWORD = 'user_php'; const BD = 'PHP'; private static $db; private $conexion; private function __construct() { $this->conexion = new mysqli(self::SERVIDOR,self::USUARIO,self::PASSWORD,self::BD); $this->conexion->set_charset('utf8'); } function __destruct() { $this->conexion->close(); } public static function obterConexion() { if (self::$db == null) { self::$db = new Database(); } return self::$db->conexion; } public static function executarSQL($consulta) { $result=self::$db->conexion->query($consulta); return $result; } public static function executarProc($procedure) { if(self::$db->conexion->multi_query($procedure)){ $result[]=self::$db->conexion->store_result(); while(self::$db->conexion->next_result()){ $result[]= self::$db->conexion->store_result(); } return $result; } else{ return null; } } public static function consultarLibros(){ $resultados=null; $result=Database::executarSQL(Database::CONSULTAR_LIBROS); if ($result && $result->num_rows>0){ while($row = $result->fetch_assoc()) { $resultados[]=$row; } $result->free(); // Liberamos da memoria os recursos } return $resultados; } }
Arquivo: Consultar_Libros.php
<?php include ('Database.php'); $conex = Database::obterConexion(); if ($conex->connect_error){ die('Erro de Conexión (' . mysqli_connect_errno() . ') ' . mysqli_connect_error()); } ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Operacións BD</title> </head> <body> <?php $result=Database::consultarLibros(); if (!empty($result)){ echo "<table border='1'>"; foreach ($result as $fila){ echo '<tr>'; printf("<td>%s</td>",$fila["titulo"]); echo '</tr>'; } echo '</table>'; } else { echo "Non hai datos que amosar!!!!"; } ?> </body> </html> <?php ob_start() ?>
- Tamén poderíamos aplicar os conceptos da POO que aprendemos anteriormente na wiki e facer que a información que devolva a función 'consultarLibros' da clase Database, sexa un array de obxectos dunha clase definida previamente, que representaría a táboa LIBROS.
- No seguinte punto imos aprender como 'separar' en diferentes 'capas' a aplicación para que o mantemento do proxecto sexa moito máis doado.
Modelo-Vista-Controlador
- O visto nos puntos anteriores mellora moito a claridade e mantemento dos programas PHP, pero a forma correcta (polo menos en proxectos grandes) sería empregar o patrón de deseño MODELO-VISTA-CONTROLADOR.
- Basicamente se basa en dividir o proxecto PHP nas seguintes partes:
- MODELOS: Onde están definidas as clases que van acceder á base de datos (pode ser outra fonte de datos, pero imos simplificalo). Cada clase fará referencia a unha táboa da base de datos.
- A maiores dos atributos (columnas na táboa) terá gardado os métodos que irán contra a base de datos a facer operacións. Para acceder á base de datos farán uso dunha clase cos métodos que conectan contra a base de datos e executan as consultas/operacións.
- VISTAS: Son as páxinas con código HTML/Javascript que vai visualizar o usuario.
- CONTROLADORES: Son as páxinas que van recibir as peticións por parte del usuario e serán encargadas de buscar os datos (empregando o modelo) e devolver as vistas (empregando as vistas) cos datos obtidos.
- A maiores, teremos un controlador frontal que será o encargado de recibir a URL coas peticións de carga das páxinas e chamar ao controlador adeucado en base a dita URL.
- Veremos LARAVEL na UD&-Programación de Servizos Web. Cando cheguedes a dita unidade xa teredes experiencia no manexo de Laravel para o desenvolvemento de aplicacións se fixéchedes as tarefas propostas no Moodle.
- Para entender mellor como funciona podedes consultar este exemplo.
Posta en práctica dun exemplo sinxelo empregando MVC
- Imos crear un proxecto PHP seguindo o patrón MVC aplicado ao exemplo da BD creada na UD3.
- Para iso imos crear a estrutura de cartafoles seguinte, supoñendo que o servidor web está apuntando a /var/www/html (lembrade dar os permisos adecuados para que o servidor web teña acceso):
- /var/www/acceso_datos: Onde vai ir a clase que se conecta á base de datos.
- /var/www/controladores: Onde vai ir as clases controladoras.
- /var/www/modelos: Onde van ir os modelos.
- /var/www/vistas: Onde van ir as vistas.
- /var/www/html/public: Onde vai ir a páxina controladora frontal. É a única á que van ter acceso os usuarios dende o navegador.
- Esta páxina vai ter o formato:
- index.php?paxina=libro => Entón cargará o que estea en ControladorLibro.php => getLibros
- index.php?paxina=libroautor => Entón cargará o que estea en ControladorLibro.php => getLibrosPorAutor
- A idea é facer algo parecido a isto:

Acceso a datos
- No cartafol /var/www/acceso_datos creamos a páxina Database.inc.php.
- O seu contido xa o vimos no punto anterior. É onde están definidas as constantes de conexión e os métodos que executan sentenzas contra a base de datos Mysql.
<?php namespace databases; use mysqli; // Necesario para que atopa a clase ao estar nun namespace Database class Database { const SERVIDOR = 'localhost'; const USUARIO = 'root'; const PASSWORD = 'root'; const BD = 'php'; private static $db; private $conexion; private function __construct() { $this->conexion = new mysqli(self::SERVIDOR,self::USUARIO,self::PASSWORD,self::BD); $this->conexion->set_charset('utf8'); } function __destruct() { if(!$this->conexion) $this->conexion->close(); } public static function obterConexion() { if (self::$db == null) { self::$db = new Database(); } return self::$db->conexion; } public static function executarSQL($consulta) { $result=self::$db->conexion->query($consulta); return $result; } public static function executarProc($procedure) { if(self::$db->conexion->multi_query($procedure)){ $result[]=self::$db->conexion->store_result(); while(self::$db->conexion->next_result()){ $result[]= self::$db->conexion->store_result(); } return $result; } else{ return null; } } }
Modelos
- No cartafol /var/www/modelos creamos a páxina Libro.php.
- Nesta clase estarán definidos todos os atributos e métodos relacionados coa clase. A maiores estarán os métodos que farán uso da clase DataBase e obterán os datos de facer as consultas.
- No noso exemplo, imos crear dous métodos que van acceder á base de datos. Un é un listado dos libros e outros é un listado de libros por autor. O código de ambas consultas xa estaba feito na UD3, no punto de opercións contra a base de datos.
<?php namespace modelos; require('../acceso_datos/Database.inc.php'); use databases\Database; class Libro{ const CONSULTAR_LIBROS='select id,titulo from libros order by titulo'; const CONSULTAR_LIBROS_POR_AUTOR = "select CONCAT(apelidos,',',nome) as nome_autor,libros.id ,titulo from libros " . ' INNER JOIN libro_autor ON (libro_autor.libro_id=libros.id) ' . ' INNER JOIN autores ON (libro_autor.autor_id=autores.id)' . " where CONCAT(autores.nome,' ',autores.apelidos) like '%%%s%%' " . 'order by nome_autor,titulo'; public $id; public $titulo; function __construct($id,$titulo){ $this->id = $id; $this->titulo = $titulo; } public static function getLibros(){ $listaLibros = []; $db = Database::obterConexion(); // Por se facemos transaccions $resultado = Database::executarSQL(Libro::CONSULTAR_LIBROS); while($fila = $resultado->fetch_assoc()) { $listaLibros[] = new Libro($fila['id'],$fila['titulo']); } return $listaLibros; } public static function getLibrosPorAutor($autor){ $listaLibros = []; $db = Database::obterConexion(); // Por se facemos transaccions $consulta = sprintf(Libro::CONSULTAR_LIBROS_POR_AUTOR,$autor); $resultado = Database::executarSQL($consulta); while($fila = $resultado->fetch_assoc()) { $listaLibros[] = new Libro($fila['id'],$fila['titulo']); } return $listaLibros; } }
Vistas
- No cartafol /var/www/vistas creamos a páxina:
- listado_libros.php: Amosará o listado de libros e fará que cada libro sexa un enlace a unha páxina de detalle (a páxina non está feita)
- listado_libros_por_autor.php: Amosará o listado de libros por autor. A mesma páxina xa ten un input onde escribir o nome do autor a buscar.
Páxina listado_libros.php
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Listado de libros</title> </head> <body> <h1>Listado de libros</h1> <ul> <?php if(count($libros)==0){ printf("<h5>SEN LIBROS</h5>"); } else{ foreach($libros as $libro){ printf("<li><a href='%s?paxina=detallelibro&id=%d'>%s</a></li>",htmlentities($_SERVER['PHP_SELF']),$libro->id,$libro->titulo); } } ?> </ul> </body> </html>
- Como vedes é moi sinxela. Vai recibir unha variable de nome $libros que estará definida no controlador. Dita variable vai ser unha lista de obxectos Libros.
Paxina listado_libros_por_autor.php:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Listado de libros</title> </head> <body> <form action="<?php echo htmlentities($_SERVER['PHP_SELF']) ?>?paxina=libroautor" method='post' > BUSCAR POR AUTOR:<input type='text' size='50' maxlength="100" required name="txtAutor" /> </form> <?php if(isset($libros)) { printf("<h1>Listado de libros</h1>"); if(count($libros)==0){ printf("<h5>SEN LIBROS CON ESE AUTOR</h5>"); } else{ printf("<ul>"); foreach($libros as $libro){ printf("<li><a href='index.php?paxina=detallelibro&id=%d'>%s</a></li>",$libro->id,$libro->titulo); } printf("</ul>"); } } ?> </body> </html>
- Nesta páxina o concepto é o mesmo que na anterior. Vai recibir do controlador un conxunto de obxectos da clase Libros. Soamente os amosará se existe a variable $libros. Se non existe quere dicir que cargamos a páxina por primeira vez e que non debe amosar o resultado de buscar.
- Se buscamos por algo, chamamos á mesma páxina index.php indicando a páxina que queremos cargar e enviando o nome do autor en $_POST['txtAutor'].
Controlador
- No cartafol /var/www/controladores creamos a páxina LibroControlador.php.
<?php namespace controlladores; include('../modelos/Libro.php'); use modelos\Libro; class LibroController{ public static function listarLibros(){ $libros = Libro::getLibros(); include('../vistas/listado_libros.php'); // Poderíamos pasarlle dentro dunha variable de sesion los libros } public static function listarLibrosPorAutor($autor){ if ($autor!=null) { $autor = htmlentities($autor); $libros = Libro::getLibrosPorAutor($autor); } include('../vistas/listado_libros_por_autor.php'); // Poderíamos pasarlle dentro dunha variable de sesion los libros } }
- Esta clase será empregada por index.php para cargar os datos que queiramos (libros ou libros por autor).
- Fixarse que cando chamemos a listarLibros() se define a variable $libros que será empregada pola vista có include que estamos a facer.
- No método listarLibrosPorAutor($autor) o $autor vai ser enviado dende index.php e vai ser o dato que ven en $_POST['txtAutor'] do formulario. A primeira vez que se carga, $autor terá o valor de null e por iso non carga os datos da base de datos.
Controlador Frontal
- No cartafol /var/www/html/public creamos a páxina index.php.
<?php // LEMBRAR QUITAR ISTO CANDO FUNCIONE ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); require_once('../controladores/LibroController.php'); use controlladores\LibroController; if(!isset($_GET['paxina'])){ die(('É necesario cargar unha páxina na url da forma paxina=libro|libroautor')); } switch($_GET['paxina']){ case 'libro': LibroController::listarLibros(); break; case 'libroautor': $autor = $_POST['txtAutor'] ?? null; // Operador que devolve null se $_POST[txtAutor] non existe. Poderíamos empregar isset //if(!isset($_POST['txtAutor'])) $autor = null; //else $autor = $_POST['txtAutor']; LibroController::listarLibrosPorAutor($autor); break; default: print("PAXINA NON PREPARADA"); }
Máis información MVC
-- Ángel D. Fernández González -- (2024).