Skip to content

4. Una aplicación de ejemplo

Nos proponemos ilustrar el método anterior con un ejemplo de cálculo de impuestos.

4.1. El problema

Nos proponemos escribir un programa que permita calcular los impuestos de un contribuyente. Nos situamos en el caso simplificado de un contribuyente que solo tiene que declarar su salario:

  • se calcula el número de tramos del asalariado nbParts=nbEnfants/2 +1 si no está casado, nbEnfants/2+2 si está casado, donde nbEnfants es el número de hijos que tiene. El número de partes se incrementa en 0,5 si hay tres hijos o más.
  • se calcula su renta imponible R = 0,72 * S, donde S es su salario anual
  • se calcula su coeficiente familiar Q = R/N
  • se calcula su impuesto I a partir de los siguientes datos
límite
coeffR
coeffN
12620,0
0
0
13190
0,05
631
15640
0,1
1290,5
24 740
0,15
2072,5
31 810
0,2
3309,5
39 970
0,25
4900
48 360
0,3
6898,5
55 790
0,35
9316,5
92 970
0,4
12106
127 860
0,45
16754,5
151 250
0,50
231 47,5
172 040
0,55
30710
195 000
0,60
39312
0
0,65
49 062
Cada línea tiene tres campos límite: coeffR y coeffN. Para calcular el impuesto I, se busca la primera línea en la que QF <= límite. Por ejemplo, si QF = 30000, se encontrará la línea: 31810 0,2 3309,5. El impuesto I es entonces igual a 0,2*R - 3309,5*nbParts. Si QF es tal que la relación QF<=límite nunca se cumple, entonces se utilizan los coeficientes de la última línea: 0 0,65 49062, lo que da el impuesto I=0,65*R - 49062*nbParts.

4.2. La base de datos

Los datos anteriores se registran en una base de datos MySQL denominada dbimpots. El usuario seldbimpots, con contraseña mdpseldbimpots, tiene acceso de solo lectura al contenido de la base de datos. Esta cuenta con una única tabla denominada impots, cuya estructura es la siguiente:

Image

Su contenido es el siguiente:

Image

4.3. La arquitectura MVC de la aplicación

La aplicación tendrá la siguiente arquitectura MVC:

  • el controlador main.php será el controlador genérico expuesto anteriormente
  • la solicitud del cliente se envía al controlador en forma de una petición del tipo main.php?action=xx. El valor del parámetro action determina el script del bloque ACTIONS que se debe ejecutar. El script de acción ejecutado devuelve al controlador una variable que indica el estado en el que debe colocarse la aplicación web. Con este estado, el controlador activará uno de los generadores de vistas para enviar la respuesta al cliente.
  • impots-data.php es la clase encargada de proporcionar al controlador los datos que necesita
  • impots-calcul.php es la clase de negocio que permite el cálculo del impuesto

4.4. La clase de acceso a datos

La clase de acceso a datos está diseñada para ocultar a la aplicación web el origen de los datos. En su interfaz, encontramos un método getData que proporciona las tres tablas de datos necesarias para el cálculo del impuesto. En nuestro ejemplo, los datos se extraen de una base de datos MySQL. Para que la clase sea independiente del tipo real de SGBD, utilizaremos la biblioteca pear::DB descrita en el anexo. El código de la clase es el siguiente:

<?php

   // bibliotecas
  require_once 'DB.php';

  class impots_data{  
     // clase de acceso a la fuente de datos DBIMPOTS

      // attributs
        var $sDSN;         // la cadena de conexión
        var $sDatabase;    // nombre de la base de datos
        var $oDB;          // conexión a la base de datos
        var $aErreurs;     // liste d'erreurs
        var $oRésultats;   // resultado de una consulta
        var $connecté;     // valor booleano que indica si se está conectado o no a la base
        var $sQuery ;      // la última consulta ejecutada

    // constructeur
    function impots_data($dDSN){

         // $dDSN: diccionario que define la conexión que se va a establecer
       // $dDSN['sgbd']: el tipo de SGBD al que hay que conectarse
       // $dDSN['host']: el nombre del servidor que lo aloja      
       // $dDSN['database']: el nombre de la base de datos a la que hay que conectarse      
       // $dDSN['user']: un usuario de la base
      // $dDSN['mdp'] : son mot de passe

       // crea en $oDB una conexión a la base de datos definida por $dDSN con la identidad de $dDSN['user']
       // si la conexión se realiza correctamente  
           // introduce en $sDSN la cadena de conexión a la base de datos
           // introduce en $sDataBase el nombre de la base de datos a la que se conecta
         // establece $connecté como verdadero
       // si la conexión falla
           // añade los mensajes de error correspondientes a la lista $aErreurs
         // cierra la conexión si es necesario
         // establece $connecté en falso 

       // borra la lista de errores
            $this->aErreurs=array();

       // se crea una conexión a la base de datos $sDSN
      $this->sDSN=$dDSN["sgbd"]."://".$dDSN["user"].":".$dDSN["mdp"]."@".$dDSN["host"]."/".$dDSN["database"];
      $this->sDatabase=$dDSN["database"];
      $this->connect();

       // ¿Conectado?
      if( ! $this->connecté) return;

       // la conexión se ha realizado correctamente     
      $this->connecté=TRUE;     
    }//constructeur

    // ------------------------------------------------------------------
    function connect(){
         // (re)conexión a la base
       // borrar lista de errores
            $this->aErreurs=array();

       // se está creando una conexión con la base de datos $sDSN
        $this->oDB=DB::connect($this->sDSN,true);

         // ¿error?
        if(DB::iserror($this->oDB)){
           // se registra el error
          $this->aErreurs[]="Echec de la connexion à la base [".$this->sDatabase."] : [".$this->oDB->getMessage()."]";
         // la conexión ha fallado
        $this->connecté=FALSE;
        // fin
        return;
      }

       // estamos conectados
      $this->connecté=TRUE;
    }//connect

    // ------------------------------------------------------------------
    function disconnect(){
       // si estamos conectados, cerramos la conexión a la base $sDSN
        if($this->connecté){
              $this->oDB->disconnect();
           // se ha desconectado
        $this->connecté=FALSE;
            }//if
    }//disconnect

    // -------------------------------------------------------------------
    function execute($sQuery){
         // $sQuery: consulta para ejecutar

         // se almacena la solicitud
            $this->sQuery=$sQuery;    

       // ¿Estamos conectados?
      if(! $this->connecté){
           // se registra el error
        $this->aErreurs[]="Pas de connexion existante à la base [$this->sDatabase]";
        // fin
        return;
      }//if

       // ejecución de la consulta
      $this->oRésultats=$this->oDB->query($sQuery);

         // ¿Error?
        if(DB::iserror($this->oRésultats)){
             // se anota el error
            $this->aErreurs[]="Echec de la requête [$sQuery] : [".$this->oRésultats->getMessage()."]";
          // retour
        return;
      }//if     
    }//execute

    // ------------------------------------------------------------------
    function getData(){
       // se recuperan las 3 series de datos: limites, coeffr, coeffn
      $this->execute('select limites, coeffR, coeffN from impots');
       // ¿Hay errores?
      if(count($this->aErreurs)!=0) return array();
       // se recorre el resultado de la selección
      while ($ligne = $this->oRésultats->fetchRow(DB_FETCHMODE_ASSOC)) {
        $limites[]=$ligne['limites'];
        $coeffr[]=$ligne['coeffR'];
        $coeffn[]=$ligne['coeffN'];                
      }//while
      return array($limites,$coeffr,$coeffn);      
    }//getDataImpots

  }//classe
?>      

Un programa de prueba podría ser el siguiente:

<?php

  // biblioteca
  require_once "c-impots-data.php";  
  require_once "DB.php";

    // prueba de la clase impots-data
  ini_set('track_errors','on');
  ini_set('display_errors','on');

    // configuration base dbimpots
    $dDSN=array(
        "sgbd"=>"mysql",
        "user"=>"seldbimpots",
        "mdp"=>"mdpseldbimpots",
        "host"=>"localhost",
        "database"=>"dbimpots"
    );

  // inicio de sesión
  $oImpots=new impots_data($dDSN);
  // erreurs ?
  if(checkErreurs($oImpots)){
    exit(0);
  }
  // suivi
  echo "Connecté à la base...\n";

  // recuperación de los datos de límites, coeffr, coeffn
  list($limites,$coeffr,$coeffn)=$oImpots->getData();
  // erreurs ?
  if( ! checkErreurs($oImpots)){
    // contenu
    echo "données : \n";
    for($i=0;$i<count($limites);$i++){
      echo "[$limites[$i],$coeffr[$i],$coeffn[$i]]\n";
    }//for
  }//if

  // se desconecta
  $oImpots->disconnect();
  // suivi
  echo "Déconnecté de la base...\n";  
  // fin
  exit(0);

  // ----------------------------------
  function checkErreurs(&$oImpots){
      // ¿Hay errores?
    if(count($oImpots->aErreurs)!=0){
        // affichage
      for($i=0;$i<count($oImpots->aErreurs);$i++){
          echo $oImpots->aErreurs[$i]."\n";
      }//for
      // ¿Hay errores?
      return true;
    }//if
    // sin errores
    return false;
  }//checkErreurs  

?>     

La ejecución de este programa de prueba da los siguientes resultados:

Connecté à la base...
données : 
[12620,0,0]
[13190,0.05,631]
[15640,0.1,1290.5]
[24740,0.15,2072.5]
[31810,0.2,3309.5]
[39970,0.25,4900]
[48360,0.3,6898]
[55790,0.35,9316.5]
[92970,0.4,12106]
[127860,0.45,16754]
[151250,0.5,23147.5]
[172040,0.55,30710]
[195000,0.6,39312]
[0,0.65,49062]
Déconnecté de la base...

4.5. La clase de cálculo del impuesto

Esta clase permite calcular el impuesto de un contribuyente. Se proporcionan a su constructor los datos necesarios para realizar este cálculo. A continuación, se encarga de calcular el impuesto correspondiente. El código de la clase es el siguiente:

<?php

  class impots_calcul{  
    // clase de cálculo del impuesto

    // constructeur
    function impots_calcul(&$perso,&$data){
      // $perso: diccionario con las siguientes claves
      // hijos: número de hijos
      // salario: salario anual
      // casado(a): valor booleano que indica si el contribuyente está casado o no
      // impuesto(s): impuesto a pagar calculado por este generador
      // $data: diccionario con las siguientes claves
      // límites: tabla de límites de tramos
      // coeficientes: tabla de coeficientes de la renta
      // coeffn: tabla de coeficientes del número de participaciones
      //: las 3 tablas tienen el mismo número de elementos

      // cálculo del número de participaciones
      if($perso['marié'])
        $nbParts=$perso['enfants']/2+2;
        else $nbParts=$perso['enfants']/2+1;
      if ($perso['enfants']>=3) $nbParts+=0.5;

      // revenu imposable
      $revenu=0.72*$perso['salaire'];

      // quotient familial
      $QF=$revenu/$nbParts;

      // búsqueda del tramo impositivo correspondiente a QF
      $nbTranches=count($data['limites']);
      $i=0;
      while($i<$nbTranches-2 && $QF>$data['limites'][$i]) $i++;

      // el impuesto
      $perso['impot']=floor($data['coeffr'][$i]*$revenu-$data['coeffn'][$i]*$nbParts);
    }//constructeur
  }//classe
?>

Un programa de prueba podría ser el siguiente:

<?php

  // biblioteca
  require_once "c-impots-data.php";
  require_once "c-impots-calcul.php";  

    // configuration base dbimpots
    $dDSN=array(
        "sgbd"=>"mysql",
        "user"=>"seldbimpots",
        "mdp"=>"mdpseldbimpots",
        "host"=>"localhost",
        "database"=>"dbimpots"
    );

  // inicio de sesión
  $oImpots=new impots_data($dDSN);
  // erreurs ?
  if(checkErreurs($oImpots)){
    exit(0);
  }
  // suivi
  echo "Connecté à la base...\n";  
  // recuperación de datos de límites, coeffr, coeffn
  list($limites,$coeffr,$coeffn)=$oImpots->getData();
  // erreurs ?
  if(checkErreurs($oImpots)){
    exit(0);
  }
  // se desconecta
  $oImpots->disconnect();
  // suivi
  echo "Déconnecté de la base...\n";  

  // cálculo de un impuesto
  $dData=array('limites'=>&$limites,'coeffr'=>&$coeffr,'coeffn'=>&$coeffn);
  $dPerso=array('enfants'=>2,'salaire'=>200000,'marié'=>true,'impot'=>0);
  new impots_calcul($dPerso,$dData);
  dump($dPerso);
  $dPerso=array('enfants'=>3,'salaire'=>200000,'marié'=>false,'impot'=>0);
  new impots_calcul($dPerso,$dData);
  dump($dPerso);
  $dPerso=array('enfants'=>3,'salaire'=>20000,'marié'=>true,'impot'=>0);
  new impots_calcul($dPerso,$dData);
  dump($dPerso);
  $dPerso=array('enfants'=>3,'salaire'=>2000000,'marié'=>true,'impot'=>0);
  new impots_calcul($dPerso,$dData);
  dump($dPerso);

  // fin
  exit(0);

  // ----------------------------------
  function checkErreurs(&$oImpots){
      // ¿Hay errores?
    if(count($oImpots->aErreurs)!=0){
        // affichage
      for($i=0;$i<count($oImpots->aErreurs);$i++){
          echo $oImpots->aErreurs[$i]."\n";
      }//for
      // ¿Hay errores?
      return true;
    }//if
    // sin errores
    return false;
  }//checkErreurs  

?>        

La ejecución de este programa de pruebas da los siguientes resultados:

Connecté à la base...
Déconnecté de la base...
[enfants,2] [salaire,200000] [marié,1] [impot,22506] 
[enfants,3] [salaire,200000] [marié,] [impot,22506] 
[enfants,3] [salaire,20000] [marié,1] [impot,0] 
[enfants,3] [salaire,2000000] [marié,1] [impot,706752]

4.6. Funcionamiento de la aplicación

Al iniciar la aplicación web de cálculo de impuestos, se obtiene la siguiente vista [v-formulaire]:

El usuario rellena los campos y solicita el cálculo del impuesto:

Cabe destacar que el formulario se regenera tal y como lo validó el usuario y que, además, muestra el importe del impuesto a pagar. El usuario puede cometer errores al introducir los datos. Estos se le señalan mediante una página de errores que denominaremos vista [v-erreurs].

El enlace [Retour au formulaire de saisie] permite al usuario recuperar el formulario tal y como lo validó.

Por último, el botón [Effacer le formulaire] restablece el formulario a su estado inicial, c.a.d, tal y como lo recibió el usuario en la solicitud inicial.

4.7. Resumen de la arquitectura MVC de la aplicación

La aplicación tiene la siguiente arquitectura:

Acabamos de describir las dos clases impots-data.php y impots-calcul.php. A continuación, describimos los demás elementos de la arquitectura.

4.8. El controlador de la aplicación

El controlador main.php de la aplicación es el que se ha descrito en la primera parte de este capítulo. Se trata de un controlador genérico independiente de la aplicación.

<?php
     // controlador genérico

  // lecture config
  include 'config.php';

  // Inclusión de bibliotecas
  for($i=0;$i<count($dConfig['includes']);$i++){
      include($dConfig['includes'][$i]);
  }//for  

  // se inicia o se reanuda la sesión
  session_start();
  $dSession=$_SESSION["session"];
  if($dSession) $dSession=unserialize($dSession);

  // se recupera la acción a realizar
  $sAction=$_GET['action'] ? strtolower($_GET['action']) : 'init';
  $sAction=strtolower($_SERVER['REQUEST_METHOD']).":$sAction";

     // ¿Es normal la secuencia de acciones?
  if( ! enchainementOK($dConfig,$dSession,$sAction)){  
    // secuencia anómala
    $sAction='enchainementinvalide';
  }//if

     // procesamiento de la acción
  $scriptAction=$dConfig['actions'][$sAction] ? 
    $dConfig['actions'][$sAction]['url'] : 
    $dConfig['actions']['actionInvalide']['url'];
  include $scriptAction;


  // envío de la respuesta (vista) al cliente
  $sEtat=$dSession['etat']['principal'];
  $scriptVue=$dConfig['etats'][$sEtat]['vue'];
  include $scriptVue;

  // fin del script: no se debería llegar hasta aquí a menos que haya un error
  trace ("Erreur de configuration.");
  trace("Action=[$sAction]");
  trace("scriptAction=[$scriptAction]");
  trace("Etat=[$sEtat]");
  trace("scriptVue=[$scriptVue]");
  trace ("Vérifiez que les script existent et que le script [$scriptVue] se termine par l'appel à finSession.");
  exit(0);

  // ---------------------------------------------------------------
  function finSession(&$dConfig,&$dReponse,&$dSession){
    // $dConfig: diccionario de configuración
      // $dSession: diccionario que contiene la información de la sesión
         // $dReponse: el diccionario de argumentos de la página de respuesta

    //: registro de la sesión
    if(isset($dSession)){
      //: se introducen los parámetros de la solicitud en la sesión
      $dSession['requete']=strtolower($_SERVER['REQUEST_METHOD'])=='get' ? $_GET :
          strtolower($_SERVER['REQUEST_METHOD'])=='post' ? $_POST : array();
        $_SESSION['session']=serialize($dSession);
      session_write_close();
    }else{    
        // sin sesión
      session_destroy();
    }

        //: se muestra la respuesta
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    // fin del script
    exit(0);
  }//finsession      

  //--------------------------------------------------------------------
    function enchainementOK(&$dConfig,&$dSession,$sAction){
      // comprueba si la acción actual está autorizada con respecto al estado anterior
    $etat=$dSession['etat']['principal'];
    if(! isset($etat)) $etat='sansetat';

    // verificación de la acción
    $actionsautorisees=$dConfig['etats'][$etat]['actionsautorisees'];
    $autorise= ! isset($actionsautorisees) || in_array($sAction,$actionsautorisees);
        return $autorise;    
  }

  //--------------------------------------------------------------------
  function dump($dInfos){
      // muestra un diccionario de información
    while(list($clé,$valeur)=each($dInfos)){
        echo "[$clé,$valeur]<br>\n";
    }//while
  }//suivi

  //--------------------------------------------------------------------
  function trace($msg){
      echo $msg."<br>\n";
  }//suivi
?>

4.9. Las acciones de la aplicación web

Hay cuatro acciones:

  • get:init: es la acción que se activa durante la solicitud inicial sin parámetros al controlador. Genera la vista «formulario» vacía.
  • post:effacerformulaire: acción activada por el botón [Effacer le formulaire]. Genera la vista [v-formulaire] vacía.
  • post:calculerimpot: acción activada por el botón [Calculer l'impôt]. Genera la vista [v-formulaire] con el importe del impuesto a pagar o la vista [v-erreurs].
  • get:retourformulaire: acción activada por el enlace [Retour au formulaire de saisie]. Genera la vista [v-formulaire] prellenada con los datos erróneos.

Estas acciones se configuran de la siguiente manera en el archivo de configuración:

<?php

  // configuración de las acciones de la aplicación
  $dConfig['actions']['get:init']=array('url'=>'a-init.php');  
  $dConfig['actions']['post:calculerimpot']=array('url'=>'a-calculimpot.php');
  $dConfig['actions']['get:retourformulaire']=array('url'=>'a-retourformulaire.php');
  $dConfig['actions']['post:effacerformulaire']=array('url'=>'a-init.php');
  $dConfig['actions']['enchainementinvalide']=array('url'=>'a-enchainementinvalide.php');
  $dConfig['actions']['actionInvalide']=array('url'=>'a-actioninvalide.php');          

A cada acción se le asocia el script encargado de procesarla. Cada acción llevará a la aplicación web a un estado definido por el elemento $dSession['etat']['principal']. Este estado está destinado a guardarse en la sesión. Además, la acción registra en el diccionario $dReponse la información necesaria para mostrar la vista relacionada con el nuevo estado en el que se encontrará la aplicación.

4.10. Los estados de la aplicación web

Hay dos:

  • [e-formulaire]: informe en el que se presentan las diferentes variantes de la vista [v-formulaire].
  • [e-erreurs]: estado en el que se presenta la vista [v-erreurs].

Las acciones permitidas en estos informes son las siguientes:

<?php

  // configuración de los estados de la aplicación
  $dConfig['etats']['formulaire']=array(
       'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
    'vue'=>'e-formulaire.php');
  $dConfig['etats']['erreurs']=array(
      'actionsautorisees'=>array('get:retourformulaire','get:init'),
      'vue'=>'e-erreurs.php');
  $dConfig['etats']['sansetat']=array('actionsautorisees'=>array('get:init'));        

En un estado, las acciones autorizadas corresponden a los URL destinos de los enlaces o botones [submit] de la vista asociada al estado. Además, la acción «get:init» está siempre autorizada. Esto permite al usuario recuperar el URL main.php de la lista de URL de su navegador y volver a reproducirlo independientemente del estado de la aplicación. Es una especie de reinicio «manual». El estado «sin estado» solo existe al iniciar la aplicación.

A cada estado de la aplicación se le asocia un script encargado de generar la vista asociada al estado:

  • estado [e-formulaire]: script e-formulaire.php
  • estado [e-erreurs]: script e-erreurs.php

El estado [e-formulaire] mostrará la vista [v-formulaire] con algunas variantes. De hecho, la vista [v-formulaire] puede aparecer vacía, prellenada o con el importe del impuesto. La acción que llevará a la aplicación al estado [e-formulaire] especifica en la variable $dSession['etat']['principal'] el estado principal de la aplicación. El controlador solo utiliza esta información. En nuestra aplicación, la acción que lleva al estado [e-formulaire] añadirá en $dSession['etat']['secondaire'] una información complementaria que permite al generador de la respuesta saber sidebe generar un formulario vacío, prellenado, con o sin el importe del impuesto. Se podría haber procedido de otra manera, estimando que había tres estados diferentes y, por lo tanto, tres generadores de vista que escribir.

4.11. El archivo de configuración de la aplicación web config.php

<?php

     // configuración de PHP
  ini_set("register_globals","off");
  ini_set("display_errors","off");  
  ini_set("expose_php","off");

  // lista de módulos que se deben incluir
  $dConfig['includes']=array('c-impots-data.php','c-impots-calcul.php');

  // controlador de la aplicación
  $dConfig['webapp']=array('titre'=>"Calculez votre impôt");

  // configuración de las vistas de la aplicación
  $dConfig['vuesReponse']['modele1']=array('url'=>'m-reponse.php');
  $dConfig['vuesReponse']['modele2']=array('url'=>'m-reponse2.php');  
  $dConfig['vues']['formulaire']=array('url'=>'v-formulaire.php');
  $dConfig['vues']['erreurs']=array('url'=>'v-erreurs.php');
  $dConfig['vues']['formulaire2']=array('url'=>'v-formulaire2.php');
  $dConfig['vues']['erreurs2']=array('url'=>'v-erreurs2.php');
  $dConfig['vues']['bandeau']=array('url'=>'v-bandeau.php');
  $dConfig['vues']['menu']=array('url'=>'v-menu.php');     
  $dConfig['style']['url']='style1.css';  

  // configuración de las acciones de la aplicación
  $dConfig['actions']['get:init']=array('url'=>'a-init.php');  
  $dConfig['actions']['post:calculerimpot']=array('url'=>'a-calculimpot.php');
  $dConfig['actions']['get:retourformulaire']=array('url'=>'a-retourformulaire.php');
  $dConfig['actions']['post:effacerformulaire']=array('url'=>'a-init.php');
  $dConfig['actions']['enchainementinvalide']=array('url'=>'a-enchainementinvalide.php');
  $dConfig['actions']['actionInvalide']=array('url'=>'a-actioninvalide.php');          

  // configuración de los informes de la aplicación
  $dConfig['etats']['e-formulaire']=array(
       'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
    'vue'=>'e-formulaire.php');
  $dConfig['etats']['e-erreurs']=array(
      'actionsautorisees'=>array('get:retourformulaire','get:init'),
      'vue'=>'e-erreurs.php');
  $dConfig['etats']['sansetat']=array('actionsautorisees'=>array('get:init'));

  // configuración del modelo de la aplicación
    $dConfig["DSN"]=array(
        "sgbd"=>"mysql",
        "user"=>"seldbimpots",
        "mdp"=>"mdpseldbimpots",
        "host"=>"localhost",
        "database"=>"dbimpots"
    );
?>

4.12. Las acciones de la aplicación web

4.12.1. Funcionamiento general de los scripts de acción

  • El controlador invoca un script de acción en función del parámetro «action» que ha recibido del cliente.
  • Tras su ejecución, el script de acción debe indicar al controlador el estado en el que debe colocarse la aplicación. Este estado debe indicarse en $dSession['etat']['principal'].
  • Un script de acción puede querer colocar información en la sesión. Lo hace colocándola en el diccionario $dSession, diccionario que el controlador guarda automáticamente en la sesión al final del ciclo solicitud-respuesta.
  • Un script de acción puede tener información que pasar a las vistas. Este aspecto es independiente del controlador. Se trata de la interfaz entre las acciones y las vistas, una interfaz propia de cada aplicación. En el ejemplo que nos ocupa, las acciones proporcionarán información a los generadores de vistas a través de un diccionario llamado $dReponse.

4.12.2. La acción get:init

Es la acción que genera el formulario vacío. El archivo de configuración muestra que será procesada por el script a-init.php:

  $dConfig['actions']['get:init']=array('url'=>'a-init.php');

El código del script a-init.php es el siguiente:

<?php
     // se muestra el formulario de entrada
  $dSession['etat']=array('principal'=>'e-formulaire', 'secondaire'=>'init');
?>  

Este script se limita a establecer en $dSession['etat']['principal'] el estado en el que debe encontrarse la aplicación, el estado [e-formulaire], y aporta en $dSession['etat']['secondaire'] una precisión sobre dicho estado. El archivo de configuración nos muestra que el controlador ejecutará el script e-formulaire.php para generar la respuesta al cliente.

<?php

  $dConfig['etats']['e-formulaire']=array(
       'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
    'vue'=>'e-formulaire.php');

El script e-formulaire.php generará la vista [v-formulaire] en sus variantes [init] y c.a.d. El formulario está vacío.

4.12.3. La acción post:calculerimpot

Es la acción que permite calcular el impuesto a partir de los datos introducidos en el formulario. El archivo de configuración indica que es el script a-calculimpot.php el que procesará esta acción:

  $dConfig['actions']['post:calculerimpot']=array('url'=>'a-calculimpot.php');

El código del script a-calculimpot.php es el siguiente:

<?php
     // solicitud de cálculo de impuestos

  // primero se comprueba la validez de los parámetros
  $sOptMarie=$_POST['optmarie'];
  if($sOptMarie!='oui' && $sOptMarie!='non'){
      $erreurs[]="L'état marital [$sOptMarie] est erroné";
  }
  $sEnfants=trim($_POST['txtenfants']);
  if(! preg_match('/^\d{1,3}$/',$sEnfants)){
      $erreurs[]="Le nombre d'enfants [$sEnfants] est erroné";
  }
  $sSalaire=trim($_POST['txtsalaire']);
  if(! preg_match('/^\d+$/',$sSalaire)){
      $erreurs[]="Le salaire annuel [$sSalaire] est erroné";
  }

  // s'il y a des erreurs, c'est fini
  if(count($erreurs)!=0){
      // preparación de la página de errores
    $dReponse['erreurs']=&$erreurs;
    $dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'saisie');
      return;
  }//if

  // los datos introducidos son correctos
  // se recuperan los datos necesarios para el cálculo del impuesto
  if(! $dSession['limites']){
      // los datos no se encuentran en la sesión
    // se recuperan de la fuente de datos
    list($erreurs,$limites,$coeffr,$coeffn)=getData($dConfig['DSN']);
    // si hay errores, se muestra la página de errores
    if(count($erreurs)!=0){
        // Preparación de la página de errores
      $dReponse['erreurs']=&$erreurs;
        $dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'database');
      return;
    }//if
    // sin errores: los datos se introducen en la sesión
    $dSession['limites']=&$limites;
    $dSession['coeffr']=&$coeffr;
    $dSession['coeffn']=&$coeffn;
  }//if

  // aquí tenemos los datos necesarios para el cálculo del impuesto
  // se calcula este
  $dData=array('limites'=>&$dSession['limites'],
      'coeffr'=>&$dSession['coeffr'],
    'coeffn'=>&$dSession['coeffn']);
  $dPerso=array('enfants'=>$sEnfants,'salaire'=>$sSalaire,'marié'=>($sOptMarie=='oui'),'impot'=>0);
  new impots_calcul($dPerso,$dData);

    // preparación de la página de respuesta
  $dSession['etat']=array('principal'=>'e-formulaire','secondaire'=>'calculimpot');
  $dReponse['impot']=$dPerso['impot'];
  return;

  //-----------------------------------------------------------------------
  function getData($dDSN){
      // conexión a la fuente de datos definida por el diccionario $dDSN
        $oImpots=new impots_data($dDSN);
    if(count($oImpots->aErreurs)!=0) return array($oImpots->aErreurs);
    // recuperación de los datos de límites, coeffr, coeffn
        list($limites,$coeffr,$coeffn)=$oImpots->getData();
         // se desconecta
        $oImpots->disconnect();
    // se devuelve el resultado
    if(count($oImpots->aErreurs)!=0) return array($oImpots->aErreurs);
        else return array(array(),$limites,$coeffr,$coeffn);
  }//getData

El script hace lo que tiene que hacer: calcular el impuesto. Dejamos al lector la tarea de descifrar el código del procesamiento. Nos interesan los estados que pueden producirse como consecuencia de esta acción:

  • Si los datos introducidos son incorrectos o se produce un error al acceder a ellos, la aplicación pasa al estado [e-erreurs]. El archivo de configuración indica que el script e-erreurs.php se encargará de generar la vista de respuesta:
<?php

  $dConfig['etats']['e-erreurs']=array(
      'actionsautorisees'=>array('get:retourformulaire','get:init'),
      'vue'=>'e-erreurs.php');
  • En todos los demás casos, la aplicación se coloca en el estado [e-formulaire] con la variante de cálculo de impuestos indicada en $dSession['etat']['secondaire']. El archivo de configuración muestra que es el script e-formulaire.php el que generará la vista de respuesta. Utilizará el valor de $dSession['etat']['secondaire'] para generar un formulario prellenado con los valores introducidos por el usuario y, además, el importe del impuesto.

4.12.4. La acción post:effacerformulaire

Está asociada por configuración al script a-init.php ya descrito.

  $dConfig['actions']['post:effacerformulaire']=array('url'=>'a-init.php');

4.12.5. La acción get:retourformulaire

Permite volver al estado [e-formulaire] desde el estado [e-erreurs]. El script a-retourformulaire.php es el que gestiona esta acción:

  $dConfig['actions']['get:retourformulaire']=array('url'=>'a-retourformulaire.php');

El script a-retourformulaire.php es el siguiente:

<?php
     // se muestra el formulario de entrada
  $dSession['etat']=array('principal'=>'e-formulaire','secondaire'=>'retourformulaire');
?>    

Simplemente se solicita que la aplicación se coloque en el estado [e-formulaire] en su variante [retourformulaire]. El archivo de configuración nos muestra que el controlador ejecutará el script e-formulaire.php para generar la respuesta al cliente.

<?php

  $dConfig['etats']['e-formulaire']=array(
       'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
    'vue'=>'e-formulaire.php');

El script e-formulaire.php generará la vista [v-formulaire] en sus variantes [retourformulaire] y c.a.d. el formulario prellenado con los valores introducidos por el usuario, pero sin el importe del impuesto.

4.13. La secuencia de acciones no es válida

Las acciones válidas a partir de un estado determinado de la aplicación se establecen mediante la configuración:

<?php

  // configuración de los estados de la aplicación
  $dConfig['etats']['e-formulaire']=array(
       'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
    'vue'=>'e-formulaire.php');
  $dConfig['etats']['e-erreurs']=array(
      'actionsautorisees'=>array('get:retourformulaire','get:init'),
      'vue'=>'e-erreurs.php');
  $dConfig['etats']['sansetat']=array('actionsautorisees'=>array('get:init'));

Ya hemos explicado esta configuración. Si se detecta una secuencia de acciones no válida, se ejecuta el script a-enchainementinvalide.php:

  $dConfig['actions']['enchainementinvalide']=array('url'=>'a-enchainementinvalide.php');

El código de este script es el siguiente:

<?php 
     // secuencia de acciones no válida
  $dReponse['erreurs']=array("Enchaînement d'actions invalide");
  $dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'enchainementinvalide');  
?>

Consiste en colocar la aplicación en el estado [e-erreurs]. En $dSession['etat']['secondaire'] proporcionamos información que será utilizada por el generador de la página de errores. Como ya hemos visto, este generador es e-erreurs.php:

<?php

  $dConfig['etats']['e-erreurs']=array(
      'actionsautorisees'=>array('get:retourformulaire','get:init'),
      'vue'=>'e-erreurs.php');

Veremos el código de este generador más adelante. La vista enviada al cliente es la siguiente:

Image

4.14. Las vistas de la aplicación

4.14.1. Visualización de la vista final

Veamos cómo el controlador envía la respuesta al cliente, una vez ejecutada la acción solicitada por este:

<?php

....
  // se inicia o se reanuda la sesión
  session_start();
  $dSession=$_SESSION["session"];
  if($dSession) $dSession=unserialize($dSession);

  // se recupera la acción a realizar
  $sAction=$_GET['action'] ? strtolower($_GET['action']) : 'init';
  $sAction=strtolower($_SERVER['REQUEST_METHOD']).":$sAction";

     // ¿Es normal la secuencia de acciones?
  if( ! enchainementOK($dConfig,$dSession,$sAction)){  
    // secuencia anómala
    $sAction='enchainementinvalide';
  }//if

     // procesamiento de la acción
  $scriptAction=$dConfig['actions'][$sAction] ? 
    $dConfig['actions'][$sAction]['url'] : 
    $dConfig['actions']['actionInvalide']['url'];
  include $scriptAction;

  // envío de la respuesta (vista) al cliente
  $sEtat=$dSession['etat']['principal'];
  $scriptVue=$dConfig['etats'][$sEtat]['vue'];
  include $scriptVue;

.....

  // ---------------------------------------------------------------
  function finSession(&$dConfig,&$dReponse,&$dSession){
    // $dConfig: diccionario de configuración
      // $dSession: diccionario que contiene la información de la sesión
         // $dReponse: el diccionario de argumentos de la página de respuesta

    // registro de la sesión
...

         // envío de la respuesta al cliente
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    // fin del script
    exit(0);
  }//finsession      

Al regresar de un script de acción, el controlador recupera en $dSession['etat']['principal'] el estado en el que debe poner la aplicación. Este estado ha sido establecido por la acción que acaba de ejecutarse. A continuación, el controlador ejecuta el generador de vistas asociado al estado. Encuentra el nombre de este en el archivo de configuración. La función del generador de vistas es la siguiente:

  • establece en $dReponse['vuereponse'] el nombre de la plantilla de respuesta que se va a utilizar. Esta información se pasará al controlador. Una plantilla es una composición de vistas elementales que, al unirse, forman la vista final.
  • prepara la información dinámica que se mostrará en la vista final. Este punto es independiente del controlador. Se trata de la interfaz entre el generador de vistas y la vista final. Es específica de cada aplicación.
  • Debe terminar obligatoriamente con la llamada a la función finSession del controlador. Esta función
    • guardará la sesión
    • enviar la respuesta

El código de la función finSession es el siguiente:

<?php

  // ---------------------------------------------------------------
  function finSession(&$dConfig,&$dReponse,&$dSession){
    // $dConfig: diccionario de configuración
      // $dSession: diccionario que contiene la información de la sesión
         // $dReponse: el diccionario de argumentos de la página de respuesta

    // registro de la sesión
...

         //: envío de la respuesta al cliente
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    // fin del script
    exit(0);
  }//finsession      

La vista enviada al usuario viene definida por la entidad $dReponse['vuereponse'], que define la plantilla que se utilizará para la respuesta final.

4.14.2. Plantilla de la respuesta

La aplicación generará sus diferentes respuestas según el siguiente modelo único:

Esta plantilla está asociada a la clave modéle1 del diccionario $dConfig['vuesreponse']:

  $dConfig['vuesReponse']['modele1']=array('url'=>'m-reponse.php');

El script m-reponse.php se encarga de generar esta plantilla:

<html>
    <head>
      <title>Application impots</title>
      <link type="text/css" href="<?php echo $dReponse['urlstyle'] ?>" rel="stylesheet" />
  </head>
  <body>
    <?php
            include $dReponse['vue1'];
        ?>
    <hr>
    <?php
            include $dReponse['vue2'];
        ?>
    </body>
</html>

Este script tiene tres elementos dinámicos colocados en un diccionario $dReponse y asociados a las siguientes claves:

  • urlstyle: URL de la hoja de estilo del modelo
  • vue1: nombre del script encargado de generar la vista vue1
  • vue2: nombre del script encargado de generar la vista vue2

Un generador de vistas que desee utilizar el modelo modèle1 deberá definir estos tres elementos dinámicos. A continuación, definimos las vistas elementales que pueden sustituir a los elementos [vue1] y [vue2] del modelo.

4.14.3. La vista elemental v-bandeau.php

El script v-bandeau.php genera una vista que se colocará en la zona [vue1]:

<table>
    <tr>
      <td><img src="univ01.gif"></td>
    <td>
      <table>
        <tr>
          <td><div class='titre'><?php echo $dReponse['titre'] ?></div></td>
        </tr>
        <tr>
            <td><div class='resultat'><?php echo $dReponse['resultat']?></div></td>
        </tr>
      </table>
    </td>
  </tr>
</table>    

El generador de vistas deberá definir dos elementos dinámicos colocados en un diccionario $dReponse y asociados a las siguientes claves:

  • título: título que se mostrará
  • resultado: importe del impuesto a pagar

4.14.4. La vista elemental v-formulaire.php

La parte [vue2] corresponde a la vista [v-formulaire] o a la vista [v-erreurs]. La vista [v-formulaire] se genera mediante el script v-formulaire.php:

<form method="post" action="main.php?action=calculerimpot">
    <table>
      <tr>
        <td class="libelle">Etes-vous marié(e)</td>
      <td class="valeur">
          <input type="radio" name="optmarie" <?php echo $dReponse['optoui'] ?> value="oui">oui
          <input type="radio" name="optmarie" <?php echo $dReponse['optnon'] ?> value="non">non        
      <td>
    <tr>
        <td class="libelle">Nombre d'enfants</td>
      <td class="valeur">
          <input type="text" class="text" name="txtenfants" size="3" value="<?php echo $dReponse['enfants'] ?>"        
      </td>
    </tr>
    <tr>
        <td class="libelle">Salaire annuel</td>
      <td class="valeur">
          <input type="text" class="text" name="txtsalaire" size="10" value="<?php echo $dReponse['salaire'] ?>"        
      </td>
    </tr>
    </table>
  <hr>
  <input type="submit" class="submit" value="Calculer l'impôt">  
</form>
<form method="post" action="main.php?action=effacerformulaire">
  <input type="submit" class="submit" value="Effacer le formulaire">
</form>                                            

Las partes dinámicas de esta vista, que deberán ser definidas por el generador de vistas, están asociadas a las siguientes claves del diccionario $dReponse:

  • optoui: estado del botón de opción denominado «optoui»
  • optnon: estado del botón de radio de nombre optnon
  • hijos: número de hijos que se debe introducir en el campo txthijos
  • salario: salario anual que se debe colocar en el campo txtsalario

4.14.5. La vista elemental v-erreurs.php

La vista [v-erreurs] es generada por el script v-erreurs.php:

Les erreurs suivantes se sont produites :
<ul>
    <?php
        for($i=0;$i<count($dReponse["erreurs"]);$i++){
            echo "<li class='erreur'>".$dReponse["erreurs"][$i]."</li>\n";
        }//for
    ?>
</ul>
<div class="info"><?php echo $dReponse["info"] ?></div>
<br>
<a href="<?php echo $dReponse["href"] ?>"><?php echo $dReponse["lien"] ?></a>

Las partes dinámicas de esta vista que debe definir el generador de vistas están asociadas a las siguientes claves del diccionario $dReponse:

  • errores: tabla de mensajes de error
  • info: mensaje de información
  • enlace: texto de un enlace
  • href: URL de destino del enlace anterior

4.14.6. La hoja de estilo

Todas las vistas están «vestidas» con una hoja de estilo. Para modificar el aspecto visual de la aplicación, se modificará su hoja de estilo. La siguiente hoja de estilo style1.css:

div.menu {
    background-color: #FFD700;
    color: #F08080;
    font-weight: bolder;
    text-align: center;
}
td.separateur {
    background: #FFDAB9;
    width: 20px;
}

table.modele2 {
    width: 600px;
}

BODY {
    background-image : url(standard.jpg);  
    margin-left : 0px;
    margin-top : 6px;
    color : #4A1919;
    font-size: 10pt;
    font-family: Arial, Helvetica, sans-serif;
    scrollbar-face-color:#F2BE7A;
    scrollbar-arrow-color:#4A1919;
    scrollbar-track-color:#FFF1CC;
    scrollbar-3dlight-color:#CBB673;
    scrollbar-darkshadow-color:#CBB673;
}

div.titre {
    font: 30pt Garamond;
    color: #FF8C00;
    background-color: Yellow;
}

table.menu {
    background-color: #ADD8E6;
}


A:HOVER {
    text-decoration: underline;
    color: #FF0000;
    background-color : transparent;
}
A:ACTIVE {
    text-decoration: underline;
    color : #BF4141;
    background-color : transparent;
}
A:VISITED {
    color : #BF4141;
    background-color : transparent;
}

.error {
    color : red;
    font-weight : bold;
}

INPUT.text {
    margin-left : 3px;
    font-size:8pt;
    font-weight:bold;
    color:#4A1919;
    background-color:#FFF6E0;
    border-right:1px solid;
    border-left:1px solid; 
    border-top:1px solid;
    border-bottom:1px solid;
}
td.libelle {
    background-color: #F0FFFF;
    color: #0000CD;
}

td.valeur {
    background-color: #DDA0DD;
}

DIV.resultat {
    background-color : #FFA07A;
    font : bold 12pt;
}

div.info {
    color: #FA8072;
}

li.erreur {
    color: #DC143C;
}

INPUT.submit {
    margin-left : 6px;
    font-size:8pt;
    font-weight:bold;
    color:#4A1919;
    background-color:#FFF1CC;
    border-right:1px solid;
    border-left:1px solid; 
    border-top:1px solid;
    border-bottom:1px solid;
}

4.15. Los generadores de vistas

4.15.1. Función de un generador de vistas

Recordemos la secuencia de código del controlador que ejecuta un generador de vistas:

<?php

     //: procesamiento de la acción
  $scriptAction=$dConfig['actions'][$sAction] ? 
    $dConfig['actions'][$sAction]['url'] : 
    $dConfig['actions']['actionInvalide']['url'];
  include $scriptAction;

  // envío de la respuesta (vista) al cliente
  $sEtat=$dSession['etat']['principal'];
  $scriptVue=$dConfig['etats'][$sEtat]['vue'];
  include $scriptVue;

Un generador de vista está vinculado al estado en el que se colocará la aplicación. La relación entre el estado y el generador de vista se establece mediante la configuración:

<?php

  // configuración de los estados de la aplicación
  $dConfig['etats']['e-formulaire']=array(
       'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
    'vue'=>'e-formulaire.php');
  $dConfig['etats']['e-erreurs']=array(
      'actionsautorisees'=>array('get:retourformulaire','get:init'),
      'vue'=>'e-erreurs.php');
  $dConfig['etats']['sansetat']=array('actionsautorisees'=>array('get:init'));

Ya hemos indicado cuál es la función de un generador de vistas. Recordémosla aquí. Un generador de vistas:

  • establece en $dReponse['vuereponse'] el nombre de la plantilla de respuesta que se va a utilizar. Esta información se pasará al controlador. Una plantilla es una composición de vistas elementales que, al unirse, forman la vista final.
  • prepara la información dinámica que se mostrará en la vista final. Este punto es independiente del controlador. Se trata de la interfaz entre el generador de vistas y la vista final. Es específica de cada aplicación.
  • Debe terminar obligatoriamente con la llamada a la función finSession del controlador. Esta función
    • guardará la sesión
    • enviar la respuesta

4.15.2. El generador de vistas asociado al informe [e-formulaire]

El script encargado de generar la vista asociada al estado [e-formulaire] se llama e-formulaire.php:

<?php

  $dConfig['etats']['e-formulaire']=array(
       'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
    'vue'=>'e-formulaire.php');

Su código es el siguiente:

<?php
  // se prepara la respuesta del formulario
  $dReponse['titre']=$dConfig['webapp']['titre'];
    $dReponse['vuereponse']='modele1';
  $dReponse['vue1']=$dConfig['vues']['bandeau']['url'];
  $dReponse['vue2']=$dConfig['vues']['formulaire']['url'];  
  $dReponse['urlstyle']=$dConfig['style']['url'];
  $dReponse['titre']=$dConfig['webapp']['titre'];

  // configuración según el tipo de formulario que se va a generar
    $type=$dSession['etat']['secondaire'];
  if($type=='init'){
      // formulaire vide
    $dReponse['optnon']='checked';
  }//if
  if($type=='calculimpot'){
      // debemos volver a mostrar los parámetros de entrada almacenados en la solicitud
    $dReponse['optoui']=$_POST['optmarie']=='oui' ? 'checked' : '';
    $dReponse['optnon']=$dReponse['optoui'] ? '' : 'checked';
    $dReponse['enfants']=$_POST['txtenfants'];
    $dReponse['salaire']=$_POST['txtsalaire'];
    $dReponse['resultat']='Impôt à payer : '.$dReponse['impot'].' F';    
  }//if
  if($type=='retourformulaire'){
      // debemos volver a mostrar los parámetros de entrada almacenados en la sesión
    $dReponse['optoui']=$dSession['requete']['optmarie']=='oui' ? 'checked' : '';
    $dReponse['optnon']=$dReponse['optoui']=='' ? 'checked' : '';  
    $dReponse['enfants']=$dSession['requete']['txtenfants'];
    $dReponse['salaire']=$dSession['requete']['txtsalaire'];
  }//if
  // enviamos la respuesta
  finSession($dConfig,$dReponse,$dSession);
?>      

Cabe señalar que el generador de vistas cumple las condiciones impuestas a un generador de vistas:

  • establecer en $dReponse['vuereponse'] la plantilla de respuesta que se va a utilizar
  • pasar información a este modelo. Aquí se pasa a través del diccionario $dReponse.
  • terminar con la llamada a la función finSession del controlador

Aquí, el modelo utilizado es «modelo1». Por lo tanto, el generador define las dos informaciones que necesita este modelo: $dReponse['vue1'] y $dReponse['vue2'].

En el caso concreto de nuestra aplicación, la vista asociada al estado [e-formulaire] depende de una información almacenada en la variable $dSession['etat']['secondaire']. Se trata de una elección de desarrollo. Otra aplicación podría optar por transmitir la información complementaria de otra manera. Por otra parte, aquí toda la información necesaria para mostrar la vista final se coloca en el diccionario $dReponse. Una vez más, se trata de una decisión que corresponde al desarrollador. El estado [e-formulaire] puede producirse tras cuatro acciones diferentes: init, calcularimpuesto, volveralformulario, borrarformulario. La vista que se va a mostrar no es exactamente la misma en todos los casos. Por ello, se han distinguido aquí tres casos en $dSession['etat']['secondaire']:

  • init: el formulario se muestra vacío
  • calculimpot: el formulario se muestra con el importe del impuesto y los datos que han dado lugar a su cálculo
  • retourformulaire: el formulario se muestra con los datos introducidos inicialmente

En el ejemplo anterior, el script e-formulaire.php utiliza esta información para presentar la respuesta según estas tres variantes.

4.15.3. La vista asociada al informe [e-erreurs]

El script encargado de generar la vista asociada al informe [e-erreurs] se llama e-erreurs.php y es el siguiente:

<?php

  // preparamos la respuesta de errores
  $dReponse['titre']=$dConfig['webapp']['titre'];
    $dReponse['vuereponse']='modele1';
  $dReponse['vue1']=$dConfig['vues']['bandeau']['url'];
  $dReponse['vue2']=$dConfig['vues']['erreurs']['url'];  
  $dReponse['urlstyle']=$dConfig['style']['url'];
  $dReponse['titre']=$dConfig['webapp']['titre'];
  $dReponse['lien']='Retour au formulaire de saisie';
  $dReponse['href']='main.php?action=retourformulaire';

  // información adicional
  $type=$dSession['etat']['secondaire'];
  if($type=='database'){
      $dReponse['info']="Veuillez avertir l'administrateur de l'application";
  }

  // se envía la respuesta
  finSession($dConfig,$dReponse,$dSession);
?>

4.15.4. Visualización de la vista final

Los dos scripts que generan las dos vistas finales terminan ambos con la llamada a la función finSession del controlador:

<?php

  function finSession(&$dConfig,&$dReponse,&$dSession){
    // $dConfig: diccionario de configuración
      // $dSession: diccionario que contiene la información de la sesión
         // $dReponse: el diccionario de argumentos de la página de respuesta

....

         // se muestra la respuesta
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    //: fin del script
    exit(0);
  }//finsession      

La vista enviada al usuario viene definida por la entidad $dReponse['vuereponse'], que define la plantilla que se utilizará para la respuesta final. Para los informes [e-formulaire] y [e-erreurs], esta plantilla se ha definido como modele1:

    $dReponse['vuereponse']='modele1';

Esta plantilla se corresponde, por configuración, con el script m-reponse.php:

  $dConfig['vuesReponse']['modele1']=array('url'=>'m-reponse.php');

4.16. Modificar la plantilla de respuesta

Suponemos aquí que se decide cambiar el aspecto visual de la respuesta enviada al cliente y nos interesa comprender las repercusiones que esto tiene a nivel del código.

4.16.1. La nueva plantilla

La estructura de la respuesta será ahora la siguiente:

Esta plantilla se llamará modele2 y el script encargado de generar esta plantilla se llamará m-reponse2.php:

  $dConfig['vuesReponse']['modele2']=array('url'=>'m-reponse2.php');

El script correspondiente a este modelo es el siguiente:

<html>
    <head>
      <title>Application impots</title>
      <link type="text/css" href="<?php echo $dReponse['urlstyle'] ?>" rel="stylesheet" />
  </head>
  <body>
      <table class='modele2'>
        <!-- Inicio del banner -->
        <tr>
          <td colspan="2"><?php    include $dReponse['vue1']; ?></td>
      </tr>
        <!-- fin bandeau -->            
      <tr>
            <!--: inicio del menú -->      
          <td><?php include $dReponse['vue2']; ?></td>
            <!-- fin menu -->
            <!-- Inicio de la zona 3 -->                        
          <td><?php include $dReponse['vue3']; ?></td>
            <!-- fin zone 3 -->        
      </tr>
   </table>
    </body>
</html>

Los elementos dinámicos de la plantilla son los siguientes:

  • $dReponse['urlstyle']: la hoja de estilo que se debe utilizar
  • $dReponse['vue1']: el script que se debe utilizar para generar [vue1]
  • $dReponse['vue2']: el script que se debe utilizar para generar [vue2]
  • $dReponse['vue3']: el script que se debe utilizar para generar [vue3]

Estos elementos deberán ser fijados por los generadores de vistas.

4.16.2. Las diferentes páginas de respuesta

La aplicación mostrará ahora las siguientes respuestas al usuario. En la llamada inicial, la página de respuesta será la siguiente:

Si el usuario proporciona datos válidos, se calcula el impuesto:

Si comete errores al introducir los datos, se muestra la página de errores:

Si utiliza el enlace [Retour au formulaire de saisie], vuelve a ver el formulario tal y como lo validó:

Si, en el caso anterior, utiliza el enlace [Réinitialiser le formulaire], se le muestra un formulario en blanco:

Cabe señalar que la aplicación utiliza las mismas acciones que antes. Solo ha cambiado el aspecto de las respuestas.

4.16.3. Las vistas elementales

La vista elemental [vue1] se asociará, al igual que en el ejemplo anterior, al script v-bandeau.php:

<table>
    <tr>
      <td><img src="univ01.gif"></td>
    <td>
      <table>
        <tr>
          <td><div class='titre'><?php echo $dReponse['titre'] ?></div></td>
        </tr>
        <tr>
            <td><div class='resultat'><?php echo $dReponse['resultat']?></div></td>
        </tr>
      </table>
    </td>
  </tr>
</table>  

Esta vista tiene dos elementos dinámicos:

  • $dReponse['titre']: título a mostrar
  • $dReponse['resultat']: importe del impuesto a pagar

La vista elemental [vue2] se asociará al siguiente script v-menu.php:

<table class="menu">
    <tr>
      <td><div class="menu">Options</div></td>
  </tr>
  <?php
      for($i=0;$i<count($dReponse['liens']);$i++){
        echo '<tr><td><div class="option"><a href="'.
          $dReponse['liens'][$i]['url'].
        '">'.$dReponse['liens'][$i]['texte']."</a></div></td></tr>\n";
    }//$i
  ?>
</table>

Esta vista tiene los siguientes elementos dinámicos:

  • $dReponse['liens']: tabla de enlaces que se mostrarán en [vue2]. Cada elemento de la tabla es un diccionario con dos claves:
    • 'url': URL de destino del enlace
    • 'texto': texto del enlace

La vista elemental [vue3] se asociará al script v-formulaire2.php si se desea mostrar el formulario de entrada, o al script v-erreurs2.php si se desea mostrar la página de errores. El código del script v-formulaire2.php es el siguiente:

<form method="post" action="main.php?action=calculerimpot">
    <table>
      <tr>
        <td class="libelle">Etes-vous marié(e)</td>
      <td class="valeur">
          <input type="radio" name="optmarie" <?php echo $dReponse['optoui'] ?> value="oui">oui
          <input type="radio" name="optmarie" <?php echo $dReponse['optnon'] ?> value="non">non        
      </td>
    </tr>
    <tr>
        <td class="libelle">Nombre d'enfants</td>
      <td class="valeur">
          <input type="text" class="text" name="txtenfants" size="3" value="<?php echo $dReponse['enfants'] ?>"        
      </td>
    </tr>
    <tr>
        <td class="libelle">Salaire annuel</td>
      <td class="valeur">
          <input type="text" class="text" name="txtsalaire" size="10" value="<?php echo $dReponse['salaire'] ?>"        
      </td>
    </tr>
    <tr>
        <td colspan="2" align="center"><input type="submit" class="submit" value="Calculer l'impôt"></td>
    </tr>
    </table>
</form>

Las partes dinámicas de esta vista, que deberán ser definidas por el generador de vistas, están asociadas a las siguientes claves del diccionario $dReponse:

  • optoui: estado del botón de radio con el nombre optoui
  • optnon: estado del botón de radio con el nombre optnon
  • hijos: número de hijos que se deben introducir en el campo txthijos
  • salaire: salario anual que se debe colocar en el campo txtsalaire

El script que genera la página de errores se llama v-erreurs2.php. Su código es el siguiente:

Les erreurs suivantes se sont produites :
<ul>
    <?php
        for($i=0;$i<count($dReponse["erreurs"]);$i++){
            echo "<li class='erreur'>".$dReponse["erreurs"][$i]."</li>\n";
        }//for
    ?>
</ul>
<div class="info"><?php echo $dReponse["info"] ?></div>

Las partes dinámicas de esta vista que debe definir el generador de vistas están asociadas a las siguientes claves del diccionario $dReponse:

  • errores: tabla de mensajes de error
  • info: mensaje de información

4.16.4. La hoja de estilo

No ha cambiado. Sigue siendo style1.css.

4.16.5. El nuevo archivo de configuración

Para introducir estas nuevas vistas, tenemos que modificar algunas líneas del archivo de configuración.

<?php

     // configuración de PHP
  ini_set("register_globals","off");
  ini_set("display_errors","off");  
  ini_set("expose_php","off");

  // lista de módulos a incluir
  $dConfig['includes']=array('c-impots-data.php','c-impots-calcul.php');

  // controlador de la aplicación
  $dConfig['webapp']=array('titre'=>"Calculez votre impôt");

  // configuración de las vistas de la aplicación
  $dConfig['vuesReponse']['modele1']=array('url'=>'m-reponse.php');
  $dConfig['vuesReponse']['modele2']=array('url'=>'m-reponse2.php');  
  $dConfig['vues']['formulaire']=array('url'=>'v-formulaire.php');
  $dConfig['vues']['erreurs']=array('url'=>'v-erreurs.php');
  $dConfig['vues']['formulaire2']=array('url'=>'v-formulaire2.php');
  $dConfig['vues']['erreurs2']=array('url'=>'v-erreurs2.php');
  $dConfig['vues']['bandeau']=array('url'=>'v-bandeau.php');
  $dConfig['vues']['menu']=array('url'=>'v-menu.php');     
  $dConfig['style']['url']='style1.css';  

  // configuración de las acciones de la aplicación
  $dConfig['actions']['get:init']=array('url'=>'a-init.php');  
  $dConfig['actions']['post:calculerimpot']=array('url'=>'a-calculimpot.php');
  $dConfig['actions']['get:retourformulaire']=array('url'=>'a-retourformulaire.php');
  $dConfig['actions']['post:effacerformulaire']=array('url'=>'a-init.php');
  $dConfig['actions']['enchainementinvalide']=array('url'=>'a-enchainementinvalide.php');
  $dConfig['actions']['actionInvalide']=array('url'=>'a-actioninvalide.php');          

  // configuración de los informes de la aplicación
  $dConfig['etats']['e-formulaire']=array(
       'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
    'vue'=>'e-formulaire2.php');
  $dConfig['etats']['e-erreurs']=array(
      'actionsautorisees'=>array('get:retourformulaire','get:init'),
      'vue'=>'e-erreurs2.php');
  $dConfig['etats']['sansetat']=array('actionsautorisees'=>array('get:init'));

  // configuración del modelo de la aplicación
    $dConfig["DSN"]=array(
        "sgbd"=>"mysql",
        "user"=>"seldbimpots",
        "mdp"=>"mdpseldbimpots",
        "host"=>"localhost",
        "database"=>"dbimpots"
    );
?>

La modificación esencial consiste en cambiar los generadores de vista asociados a los informes [e-formulaire] y [e-erreurs]. Una vez hecho esto, los nuevos generadores de vista se encargan de generar las nuevas páginas de respuesta.

4.16.6. El generador de vistas asociado al informe [e-formulaire]

En el archivo de configuración, el informe [e-formulaire] está ahora asociado al generador de vistas e-formulaire2.php. El código de este script es el siguiente:

<?php
  // se prepara la respuesta del formulario
  $dReponse['titre']=$dConfig['webapp']['titre'];
    $dReponse['vuereponse']='modele2';
  $dReponse['vue1']=$dConfig['vues']['bandeau']['url'];
  $dReponse['vue2']=$dConfig['vues']['menu']['url'];
  $dReponse['vue3']=$dConfig['vues']['formulaire2']['url'];
  $dReponse['urlstyle']=$dConfig['style']['url'];
  $dReponse['titre']=$dConfig['webapp']['titre'];
  $dReponse['liens']=array(
      array('texte'=>'Réinitialiser le formulaire', 'url'=>'main.php?action=init')
  );              

  // configuración según el tipo de formulario que se va a generar
    $type=$dSession['etat']['secondaire'];
  if($type=='init'){
      // formulaire vide
    $dReponse['optnon']='checked';
  }//if
  if($type=='calculimpot'){
      // debemos volver a mostrar los parámetros de entrada almacenados en la consulta
    $dReponse['optoui']=$_POST['optmarie']=='oui' ? 'checked' : '';
    $dReponse['optnon']=$dReponse['optoui'] ? '' : 'checked';
    $dReponse['enfants']=$_POST['txtenfants'];
    $dReponse['salaire']=$_POST['txtsalaire'];
    $dReponse['resultat']='Impôt à payer : '.$dReponse['impot'].' F';
  }//if
  if($type=='retourformulaire'){
      // debemos volver a mostrar los parámetros de entrada almacenados en la sesión
    $dReponse['optoui']=$dSession['requete']['optmarie']=='oui' ? 'checked' : '';
    $dReponse['optnon']=$dReponse['optoui']=='' ? 'checked' : '';  
    $dReponse['enfants']=$dSession['requete']['txtenfants'];
    $dReponse['salaire']=$dSession['requete']['txtsalaire'];
  }//if
  // enviamos la respuesta
  finSession($dConfig,$dReponse,$dSession);
?>  

Las principales modificaciones son las siguientes:

  • el generador de vistas indica que quiere utilizar el modelo de respuesta modele2
  • por este motivo, rellena los elementos dinámicos $dReponse['vue1'], $dReponse['vue2'], $dReponse['vue3'], los tres necesarios para el modelo de respuesta modele2.
  • El generador también rellena el elemento dinámico $dReponse['liens'], que establece los enlaces que se mostrarán en el área [vue2] de la respuesta.

4.16.7. El generador de vista asociado al informe [e-erreurs]

En el archivo de configuración, el informe [e-erreurs] está ahora asociado al generador de vista e-erreurs2.php. El código de este script es el siguiente:

<?php

  // preparamos la respuesta de errores
  $dReponse['titre']=$dConfig['webapp']['titre'];
    $dReponse['vuereponse']='modele2';
  $dReponse['vue1']=$dConfig['vues']['bandeau']['url'];
  $dReponse['vue2']=$dConfig['vues']['menu']['url'];  
  $dReponse['vue3']=$dConfig['vues']['erreurs2']['url'];  
  $dReponse['urlstyle']=$dConfig['style']['url'];
  $dReponse['titre']=$dConfig['webapp']['titre'];
  $dReponse['liens']=array(
      array('texte'=>'Retour au formulaire de saisie', 'url'=>'main.php?action=retourformulaire')
  );              

  // información adicional
  $type=$dSession['etat']['secondaire'];
  if($type=='database'){
      $dReponse['info']="Veuillez avertir l'administrateur de l'application";
  }

  // se envía la respuesta
  finSession($dConfig,$dReponse,$dSession);
?>  

Las modificaciones realizadas son idénticas a las realizadas en el generador de vistas e-formulaire2.php.

4.17. Conclusión

Hemos podido demostrar, con un ejemplo, las ventajas de nuestro controlador genérico. No hemos tenido que escribirlo. Nos hemos limitado a escribir los scripts de las acciones, los generadores de vista y las vistas de la aplicación. Además, hemos demostrado las ventajas de separar las acciones de las vistas. De este modo, hemos podido cambiar el aspecto de las respuestas sin modificar ni una sola línea de código de los scripts de acción. Solo se modificaron los scripts implicados en la generación de las vistas. Para que esto sea posible, el script de acción no debe hacer ninguna suposición sobre la vista que va a visualizar la información que ha calculado. Debe limitarse a entregar esta información al controlador, que la transmite al generador de vistas, el cual se encargará de darle forma. Es una regla absoluta: una acción debe estar completamente desconectada de las vistas.

En este capítulo nos hemos acercado a la filosofía Struts, bien conocida por los desarrolladores de Java. Un proyecto de código abierto llamado php.mvc permite desarrollar aplicaciones web/PHP siguiendo la filosofía Struts. Para más información, consulte el sitio web http://www.phpmvc.net/.