Diferencia entre revisiones de «PHP Conceptos básicos Modelo-Vista-Controlador»

De MediaWiki
Ir a la navegación Ir a la búsqueda
Línea 22: Línea 22:
 
* 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.
  
−
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
+
::<syntaxhighlight lang="java" enclose="div" highlight="" >
 
<?php
 
<?php
 
class Database {
 
class Database {
Línea 78: Línea 78:
  
 
* 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:
 
* 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:
−
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
+
::<syntaxhighlight lang="java" enclose="div" highlight="" >
 
     <?php
 
     <?php
 
     include ('Database.php');
 
     include ('Database.php');
Línea 130: Línea 130:
  
 
'''Arquivo: Database.php'''
 
'''Arquivo: Database.php'''
−
::<syntaxhighlight lang="java" line enclose="div" highlight="48-60" >
+
::<syntaxhighlight lang="java" enclose="div" highlight="48-60" >
 
<?php
 
<?php
 
class Database {
 
class Database {
Línea 196: Línea 196:
  
 
'''Arquivo: Consultar_Libros.php'''
 
'''Arquivo: Consultar_Libros.php'''
−
::<syntaxhighlight lang="java" line enclose="div" highlight="23" >
+
::<syntaxhighlight lang="java" enclose="div" highlight="23" >
 
     <?php
 
     <?php
 
     include ('Database.php');
 
     include ('Database.php');
Línea 253: Línea 253:
  
 
'''Arquivo:MeuSitio.css''': Arquivo de css que será aplicado a todas as páxinas do sitio web. Defínense o aspecto e tamaño de cada bloque que conforma o sitio web.
 
'''Arquivo:MeuSitio.css''': Arquivo de css que será aplicado a todas as páxinas do sitio web. Defínense o aspecto e tamaño de cada bloque que conforma o sitio web.
−
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
+
::<syntaxhighlight lang="java" enclose="div" highlight="" >
 
#cabecera{
 
#cabecera{
 
      
 
      
Línea 283: Línea 283:
  
 
'''Arquivo:Cabecera.inc.php''': Cabeceira do sitio web. Podería ter código PHP.
 
'''Arquivo:Cabecera.inc.php''': Cabeceira do sitio web. Podería ter código PHP.
−
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
+
::<syntaxhighlight lang="java" enclose="div" highlight="" >
 
<div id='cabecera'>ISTO É A CABECEIRA DO NOSO SITIO WEB
 
<div id='cabecera'>ISTO É A CABECEIRA DO NOSO SITIO WEB
 
<?php
 
<?php
Línea 295: Línea 295:
  
 
'''Arquivo:Menu.inc.php''': Arquivo que representa o zona de menús do sitio web. Podería ter código PHP.
 
'''Arquivo:Menu.inc.php''': Arquivo que representa o zona de menús do sitio web. Podería ter código PHP.
−
::<syntaxhighlight lang="java" line enclose="div" highlight="" >
+
::<syntaxhighlight lang="java" enclose="div" highlight="" >
 
<div id='menus'>  <!--Exemplo de menu. Os enlaces non funcionan-->
 
<div id='menus'>  <!--Exemplo de menu. Os enlaces non funcionan-->
 
<a href='Inicial.php'>Incio</a>
 
<a href='Inicial.php'>Incio</a>
Línea 304: Línea 304:
  
 
'''Arquivo:Consultar_Libros.php'''
 
'''Arquivo:Consultar_Libros.php'''
−
::<syntaxhighlight lang="java" line enclose="div" highlight="21-23,42" >
+
::<syntaxhighlight lang="java" enclose="div" highlight="21-23,42" >
 
     <?php
 
     <?php
 
     include ('Database.php');
 
     include ('Database.php');
Línea 367: Línea 367:
 
: Vexamos un exemplo:
 
: Vexamos un exemplo:
 
'''Arquivo:Layout.php''': Vai ser a páxina 'modelo' de todas as páxinas do noso sitio web.
 
'''Arquivo:Layout.php''': Vai ser a páxina 'modelo' de todas as páxinas do noso sitio web.
−
::<syntaxhighlight lang="java" line enclose="div" highlight="24" >
+
::<syntaxhighlight lang="java" enclose="div" highlight="24" >
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
Línea 403: Línea 403:
  
 
'''Arquivo:Consultar_Libros_Con_Modelo.php''': Esta sería a forma de cargar cada unha das páxinas.
 
'''Arquivo:Consultar_Libros_Con_Modelo.php''': Esta sería a forma de cargar cada unha das páxinas.
−
::<syntaxhighlight lang="java" line enclose="div" highlight="1,28,30" >
+
::<syntaxhighlight lang="java" enclose="div" highlight="1,28,30" >
 
<?php ob_start() ?>
 
<?php ob_start() ?>
  

Revisión del 20:35 25 ene 2024

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() {
            $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&oacute;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&oacute;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.

Uso de 'modelos' de páxinas

  • Outra mellora podería ser empregar 'modelos' para o sitio web.
Normalmente todas as páxinas terán a mesma estrutura de bloques (cabeceira, menús, contido), polo que, en vez de repetir o código en todas elas, poderíamos empregar un modelo que sexa utilizado en todas.


Opción 1

  • Unha forma sería utilizar arquivos de include para cada bloque, da forma (aplicado ao exemplo anterior):

Arquivo:MeuSitio.css: Arquivo de css que será aplicado a todas as páxinas do sitio web. Defínense o aspecto e tamaño de cada bloque que conforma o sitio web.

#cabecera{
    
    width:100%;
    height:25%;
    margin:0px auto;
    text-align: center;
    background-color: blue;
    
}

#menus{
    float:left;
    height:5%;
    background-color: red;
    font-weight: bold;
    color: yellow;
}

#contido{
    clear:both;
    width:100%;
    background-color: green;
    
    
}


Arquivo:Cabecera.inc.php: Cabeceira do sitio web. Podería ter código PHP.

<div id='cabecera'>ISTO É A CABECEIRA DO NOSO SITIO WEB
<?php
    echo "<p>Poderíamos engadir código php</p>";
?>

</div>


Arquivo:Menu.inc.php: Arquivo que representa o zona de menús do sitio web. Podería ter código PHP.

<div id='menus'>  <!--Exemplo de menu. Os enlaces non funcionan-->
<a href='Inicial.php'>Incio</a>
<a href='Consulta_Libros.php'>Consultar Libros</a>
</div>


Arquivo:Consultar_Libros.php

    <?php
    include ('Database.php');

    $conex = Database::obterConexion();
    if ($conex->connect_error){
         die('Erro de Conexi&oacute;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>
            <link href="MeuSitio.css" rel="stylesheet" />
        </head>

        <body>
            <?php include('Cabecera.inc.php'); ?>
            <?php include('Menu.inc.php'); ?>
            <div id='contido'>
            <?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!!!!";
                }

            ?>        
            </div>
        </body>    
    </html>



Opción 2

  • En PHP temos outra forma de facer 'modelos' de páxinas. O procedemento é ao revés do feito no exemplo anterior.
Cada páxina do noso sitio web estará formado por un 'modelo' de páxina (sería a páxina na que se definen os bloques) ao que se engade o contido específico da páxina a cargar.
Para facelo desta forma temos que utilizar as funcións:


Vexamos un exemplo:

Arquivo:Layout.php: Vai ser a páxina 'modelo' de todas as páxinas do noso sitio web.

<!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>Optimizando sitio web</title>
        <link href="MeuSitio.css" rel="stylesheet" />
    </head>

    <body>
        <div id='cabecera'>ISTO É A CABECEIRA DO NOSO SITIO WEB
            <?php
                echo "<p>Poderíamos engadir código php</p>";
            ?>

        </div>
        <div id='menus'>  <!--Exemplo de menu. Os enlaces non funcionan-->
            <a href='Inicial.php'>Incio</a>
            <a href='Consulta_Libros.php'>Consultar Libros</a>
        </div>
        
        
        <div id='contido'>
            <?php echo $contido ?>
        </div>

    </body>

</html>
  • Liña 24: Fixarse que o contido vai vir nunha variable de nome $contido


Arquivo:Consultar_Libros_Con_Modelo.php: Esta sería a forma de cargar cada unha das páxinas.

<?php ob_start() ?>

<?php
    
    include ('Database.php');

    $conex = Database::obterConexion();
    if ($conex->connect_error){
         die('Erro de Conexi&oacute;n (' . mysqli_connect_errno() . ') ' . mysqli_connect_error());
    }

    $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!!!!";
    }

?>        
<?php $contido = ob_get_clean() ?>

<?php include 'Layout.php' ?>
  • Liña 1,28: Utilizamos as funcións indicadas anteriormente.
Estas funcións o que fan basicamente é que a saída que fagamos (echo, printf,...) non se mande ao navegador do usuario, se non que se garde nun 'buffer'. A función ob_get_clean() devolve o contido do buffer (no noso caso o gardamos na variable $contido) e limpa o buffer.
Liña 29: Unha vez feito o anterior temos na variable $contido o resultado de executar o código PHP. Dita variable se utiliza na modelo 'Layout.php' para colocar o contido no seu sitio.



  • Como vemos, a idea é ir separando aspectos funcionais do sitio web das páxinas que se amosan ao usuario.
Aínda temos aspectos que mesturan aspectos do acceso a datos da presentación do usuario, como pode ser a validación de campos.


  • O conxunto de validacións, xunto coas regras que poidamos ter para acceder aos datos da nosa base de datos conforman as regras de negocio. No MVC dita función atópase na capa de MODELO como veremos a continuación (unha capa vai ser unha ou varias clases que van ter unha función específica).
Por exemplo, chamar a unha función que consultara os libros dun determinado autor. Dita función comprobaría previamente se o autor existe e,en caso de que o autor non exista, avisar a quen fixo a chamada. Esta función sería a encargada de facer a orde SQL contra a base de datos e devolvería un array de datos co resultado da chamada ou un código indicando que o autor non existe (regra de negocio). Tamén se encargaría de validar que o dato enviado (o autor a buscar) sexa do tipo e formato correcto (enviar o nome e apelidos, por exemplo).

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.


  • Esta forma de programar é a que empregan os frameworks servidor de PHP, como LARAVEL e SYMFONY.
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.




Posta en práctica dun exemplo sinxelo empregando MVC


Máis información MVC






-- Ángel D. Fernández González -- (2024).