Skip to content

4. Uma aplicação ilustrativa

Propomos ilustrar o método anterior com um exemplo de cálculo de impostos.

4.1. O problema

Pretendemos escrever um programa que permita calcular o imposto de um contribuinte. Consideramos o caso simplificado de um contribuinte que tenha apenas o seu salário para declarar:

  • calcula-se o número de quotas do trabalhador nbParts = nbEnfants/2 + 1 se não for casado, nbEnfants/2 + 2 se for casado, em que nbEnfants é o número de filhos. O número de quotas é aumentado em 0,5 se houver três ou mais filhos.
  • calcula-se o seu rendimento tributável R = 0,72 * S, em que S é o seu salário anual
  • calcula-se o seu coeficiente familiar Q = R/N
  • calcula-se o seu imposto I com base nos seguintes dados
limite
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
16 754,5
151 250
0,50
23 147,5
172040
0,55
30710
195 000
0,60
39312
0
0,65
49062
Cada linha tem 3 campos de limite: coeffR, coeffN. Para calcular o imposto I, procura-se a primeira linha em que QF ≤ limite. Por exemplo, se QF = 30000, encontrar-se-á a linha: 31810 0,2 3309,5. O imposto I é, então, igual a 0,2*R - 3309,5*nbParts. Se QF for tal que a relação QF <= limite nunca for verificada, então são utilizados os coeficientes da última linha: 0 0,65 49062, o que dá o imposto I = 0,65*R - 49062*nbParts.

4.2. A base de dados

Os dados anteriores são registados numa base de dados MySQL denominada dbimpots. O utilizador seldbimpots, com a palavra-passe mdpseldbimpots, tem acesso de leitura ao conteúdo da base de dados. Esta possui uma única tabela denominada impots, cuja estrutura é a seguinte:

Image

O seu conteúdo é o seguinte:

Image

4.3. A arquitetura MVC da aplicação

A aplicação terá a seguinte arquitetura MVC:

  • O controlador main.php será o controlador genérico apresentado anteriormente
  • o pedido do cliente é enviado ao controlador sob a forma de uma solicitação com o formato main.php?action=xx. O valor do parâmetro «action» determina o script do bloco ACTIONS a ser executado. O script de ação executado devolve ao controlador uma variável que indica o estado em que a aplicação web deve ser colocada. Com base nesse estado, o controlador ativará um dos geradores de vistas para enviar a resposta ao cliente.
  • impots-data.php é a classe responsável por fornecer ao controlador os dados de que este necessita
  • impots-calcul.php é a classe de negócio que permite o cálculo do imposto

4.4. A classe de acesso aos dados

A classe de acesso aos dados foi concebida para ocultar à aplicação web a origem dos dados. Na sua interface, encontra-se um método getData que fornece os três tabuleiros de dados necessários ao cálculo do imposto. No nosso exemplo, os dados são obtidos de uma base de dados MySQL. Para tornar a classe independente do tipo real do SGBD, utilizaremos a biblioteca pear::DB descrita no anexo. O código da classe é o seguinte:

<?php

  // bibliotecas
  require_once 'DB.php';

  class impots_data{  
    // classe de acesso à fonte de dados DBIMPOTS

      // atributos
    var $sDSN;                    // a cadeia de ligação
      var $sDatabase;            // o nome da base de dados
    var $oDB;                        // ligação à base de dados
    var $aErreurs;            // lista de erros
    var $oRésultats;        // resultado de uma consulta
        var $connecté;            // valor booleano que indica se está ou não ligado à base de dados
        var $sQuery;                // a última consulta executada

    // construtor
    function impots_data($dDSN){

        // $dDSN: dicionário que define a ligação a estabelecer
      // $dDSN['sgbd']: o tipo de SGBD ao qual é necessário ligar-se
      // $dDSN['host']: o nome do servidor que o aloja      
      // $dDSN['database']: o nome da base de dados à qual é necessário ligar-se      
      // $dDSN['user']: um utilizador da base de dados
      // $dDSN['mdp']: a sua palavra-passe

      // cria em $oDB uma ligação à base de dados definida por $dDSN sob a identidade de $dDSN['user']
      // se a ligação for bem-sucedida  
          // insere em $sDSN a cadeia de ligação à base de dados
          // insere em $sDataBase o nome da base de dados à qual se está a ligar
        // define $connecté como verdadeiro
      // se a ligação falhar
          // insere as mensagens de erro adequadas na lista $aErreurs
        // encerra a ligação, se necessário
        // define $connecté como falso 

      // limpa a lista de erros
            $this->aErreurs=array();

      // é criada uma ligação à base de dados $sDSN
      $this->sDSN=$dDSN["sgbd"]."://".$dDSN["user"].":".$dDSN["mdp"]."@".$dDSN["host"]."/".$dDSN["database"];
      $this->sDatabase=$dDSN["database"];
      $this->connect();

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

      // a ligação foi bem-sucedida     
      $this->connecté=TRUE;     
    }//criador

    // ------------------------------------------------------------------
    function connect(){
        // (re)ligação à base de dados
      // limpar lista de erros
            $this->aErreurs=array();

      // a criar uma ligação à base de dados $sDSN
        $this->oDB=DB::connect($this->sDSN,true);

        // erro?
        if(DB::iserror($this->oDB)){
          // registo do erro
          $this->aErreurs[]="Echec de la connexion à la base [".$this->sDatabase."] : [".$this->oDB->getMessage()."]";
        // a ligação falhou
        $this->connecté=FALSE;
        // fim
        return;
      }

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

    // ------------------------------------------------------------------
    function disconnect(){
      // se estivermos ligados, encerramos a ligação à base $sDSN
        if($this->connecté){
              $this->oDB->disconnect();
          // estamos desconectados
        $this->connecté=FALSE;
            }//se
    }//desligar

    // -------------------------------------------------------------------
    function execute($sQuery){
        // $sQuery: consulta a executar

        // a consulta é guardada
            $this->sQuery=$sQuery;    

      // está-se ligado?
      if(! $this->connecté){
          // regista-se o erro
        $this->aErreurs[]="Pas de connexion existante à la base [$this->sDatabase]";
        // fim
        return;
      }//if

      // execução da consulta
      $this->oRésultats=$this->oDB->query($sQuery);

        // erro?
        if(DB::iserror($this->oRésultats)){
            // regista-se o erro
            $this->aErreurs[]="Echec de la requête [$sQuery] : [".$this->oRésultats->getMessage()."]";
          // retorno
        return;
      }//if     
    }//executar

    // ------------------------------------------------------------------
    function getData(){
      // recuperam-se as 3 séries de dados: limites, coeffr, coeffn
      $this->execute('select limites, coeffR, coeffN from impots');
      // erros?
      if(count($this->aErreurs)!=0) return array();
      // percorre-se o resultado da consulta
      while ($ligne = $this->oRésultats->fetchRow(DB_FETCHMODE_ASSOC)) {
        $limites[]=$ligne['limites'];
        $coeffr[]=$ligne['coeffR'];
        $coeffn[]=$ligne['coeffN'];                
      }//enquanto
      return array($limites,$coeffr,$coeffn);      
    }//getDataImpots

  }//classe
?>      

Um programa de teste poderia ser o seguinte:

<?php

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

    // teste da classe impots-data
  ini_set('track_errors','on');
  ini_set('display_errors','on');

     // configuração básica do dbimpots
    $dDSN=array(
        "sgbd"=>"mysql",
        "user"=>"seldbimpots",
        "mdp"=>"mdpseldbimpots",
        "host"=>"localhost",
        "database"=>"dbimpots"
    );

  // início de sessão
  $oImpots=new impots_data($dDSN);
  // erros?
  if(checkErreurs($oImpots)){
    exit(0);
  }
  // acompanhamento
  echo "Connecté à la base...\n";

  // recuperação dos dados de limites, coeffr, coeffn
  list($limites,$coeffr,$coeffn)=$oImpots->getData();
  // erros?
  if( ! checkErreurs($oImpots)){
    // conteúdo
    echo "données : \n";
    for($i=0;$i<count($limites);$i++){
      echo "[$limites[$i],$coeffr[$i],$coeffn[$i]]\n";
    }//for
  }//if

  // desligar-se
  $oImpots->disconnect();
  // acompanhamento
  echo "Déconnecté de la base...\n";  
  // fim
  exit(0);

  // ----------------------------------
  function checkErreurs(&$oImpots){
      // erros?
    if(count($oImpots->aErreurs)!=0){
        // visualização
      for($i=0;$i<count($oImpots->aErreurs);$i++){
          echo $oImpots->aErreurs[$i]."\n";
      }//para
      // erros
      return true;
    }//if
    // sem erros
    return false;
  }//checkErreurs  

?>     

A execução deste programa de teste produz os seguintes 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. A classe de cálculo do imposto

Esta classe permite calcular o imposto de um contribuinte. São fornecidos ao seu construtor os dados necessários para esse cálculo. Em seguida, este encarrega-se de calcular o imposto correspondente. O código da classe é o seguinte:

<?php

  class impots_calcul{  
    // classe de cálculo do imposto

    // construtor
    function impots_calcul(&$perso,&$data){
      // $perso: dicionário com as seguintes chaves
      // filhos(a): número de filhos
      // salário: salário anual
      // casado(a): valor booleano que indica se o contribuinte é casado ou não
      // imposto(s): imposto a pagar calculado por este construtor
      // $data: dicionário com as seguintes chaves
      // limites: tabela dos limites das faixas de rendimento
      // coeficientes: tabela dos coeficientes do rendimento
      // coeffn: tabela dos coeficientes do número de quotas
      // as 3 tabelas têm o mesmo número de elementos

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

      // rendimento tributável
      $revenu=0.72*$perso['salaire'];

      // quociente familiar
      $QF=$revenu/$nbParts;

      // procura da faixa de imposto correspondente a QF
      $nbTranches=count($data['limites']);
      $i=0;
      while($i<$nbTranches-2 && $QF>$data['limites'][$i]) $i++;

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

Um programa de teste poderia ser o seguinte:

<?php

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

     // configuração de base do dbimpots
    $dDSN=array(
        "sgbd"=>"mysql",
        "user"=>"seldbimpots",
        "mdp"=>"mdpseldbimpots",
        "host"=>"localhost",
        "database"=>"dbimpots"
    );

  // início de sessão
  $oImpots=new impots_data($dDSN);
  // erros?
  if(checkErreurs($oImpots)){
    exit(0);
  }
  // acompanhamento
  echo "Connecté à la base...\n";  
  // recuperação de dados de limites, coeffr, coeffn
  list($limites,$coeffr,$coeffn)=$oImpots->getData();
  // erros?
  if(checkErreurs($oImpots)){
    exit(0);
  }
  // desligar sessão
  $oImpots->disconnect();
  // acompanhamento
  echo "Déconnecté de la base...\n";  

  // cálculo de um imposto
  $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);

  // fim
  exit(0);

  // ----------------------------------
  function checkErreurs(&$oImpots){
      // erros?
    if(count($oImpots->aErreurs)!=0){
        // visualização
      for($i=0;$i<count($oImpots->aErreurs);$i++){
          echo $oImpots->aErreurs[$i]."\n";
      }//para
      // erros
      return true;
    }//if
    // sem erros
    return false;
  }//checkErreurs  

?>        

A execução deste programa de testes produz os seguintes 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. Funcionamento da aplicação

Quando a aplicação web de cálculo de impostos é iniciada, é apresentada a seguinte vista [v-formulaire]:

O utilizador preenche os campos e solicita o cálculo do imposto:

Note-se que o formulário é regenerado no estado em que o utilizador o validou e que, além disso, apresenta o montante do imposto a pagar. O utilizador pode cometer erros de introdução de dados. Estes são-lhe assinalados por uma página de erros a que chamaremos vista [v-erreurs].

A ligação [Retour au formulaire de saisie] permite ao utilizador recuperar o formulário tal como o validou.

Por fim, o botão [Effacer le formulaire] repõe o formulário no seu estado inicial, c.a.d, tal como o utilizador o recebeu na solicitação inicial.

4.7. Análise da arquitetura MVC da aplicação

A aplicação tem a seguinte arquitetura MVC:

Acabámos de descrever as duas classes impots-data.php e impots-calcul.php. Passamos agora a descrever os restantes elementos da arquitetura.

4.8. O controlador da aplicação

O controlador main.php da aplicação é aquele que foi descrito na primeira parte deste capítulo. Trata-se de um controlador genérico independente da aplicação.

<?php
     // controlador genérico

  // leitura da configuração
  include 'config.php';

  // inclusão de bibliotecas
  for($i=0;$i<count($dConfig['includes']);$i++){
      include($dConfig['includes'][$i]);
  }//para  

  // inicia ou retoma a sessão
  session_start();
  $dSession=$_SESSION["session"];
  if($dSession) $dSession=unserialize($dSession);

  // recupera-se a ação a realizar
  $sAction=$_GET['action'] ? strtolower($_GET['action']) : 'init';
  $sAction=strtolower($_SERVER['REQUEST_METHOD']).":$sAction";

     // a sequência de ações está normal?
  if( ! enchainementOK($dConfig,$dSession,$sAction)){  
    // sequência anormal
    $sAction='enchainementinvalide';
  }//if

     // processamento da ação
  $scriptAction=$dConfig['actions'][$sAction] ? 
    $dConfig['actions'][$sAction]['url'] : 
    $dConfig['actions']['actionInvalide']['url'];
  include $scriptAction;


  // envio da resposta (visualização) ao cliente
  $sEtat=$dSession['etat']['principal'];
  $scriptVue=$dConfig['etats'][$sEtat]['vue'];
  include $scriptVue;

  // fim do script — não se deve chegar aqui, a menos que haja um bug
  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: dicionário de configuração
      // $dSession: dicionário contendo as informações da sessão
         // $dReponse: o dicionário de argumentos da página de resposta

    // registo da sessão
    if(isset($dSession)){
      //: os parâmetros da solicitação são colocados na sessão
      $dSession['requete']=strtolower($_SERVER['REQUEST_METHOD'])=='get' ? $_GET :
          strtolower($_SERVER['REQUEST_METHOD'])=='post' ? $_POST : array();
        $_SESSION['session']=serialize($dSession);
      session_write_close();
    }else{    
        // sem sessão
      session_destroy();
    }

        // apresenta-se a resposta
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    // fim do script
    exit(0);
  }//fim da sessão      

  //--------------------------------------------------------------------
    function enchainementOK(&$dConfig,&$dSession,$sAction){
      // verifica se a ação atual está autorizada em relação ao estado anterior
    $etat=$dSession['etat']['principal'];
    if(! isset($etat)) $etat='sansetat';

    // verificação da ação
    $actionsautorisees=$dConfig['etats'][$etat]['actionsautorisees'];
    $autorise= ! isset($actionsautorisees) || in_array($sAction,$actionsautorisees);
        return $autorise;    
  }

  //--------------------------------------------------------------------
  function dump($dInfos){
      // exibe um dicionário de informações
    while(list($clé,$valeur)=each($dInfos)){
        echo "[$clé,$valeur]<br>\n";
    }//while
  }//acompanhamento

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

4.9. As ações da aplicação web

Existem quatro ações:

  • get:init: é a ação que é acionada durante o pedido inicial sem parâmetros ao controlador. Gera a vista «formulário» vazia.
  • post:effacerformulaire: ação acionada pelo botão [Effacer le formulaire]. Gera a vista [v-formulaire] vazia.
  • post:calculerimpot: ação acionada pelo botão [Calculer l'impôt]. Gera ou a vista [v-formulaire] com o montante do imposto a pagar, ou a vista [v-erreurs].
  • get:retourformulaire: ação acionada pelo link [Retour au formulaire de saisie]. Gera a vista [v-formulaire] pré-preenchida com os dados errados.

Estas ações estão configuradas da seguinte forma no ficheiro de configuração:

<?php

  // configuração das ações da aplicação
  $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 ação está associado o script responsável pelo seu processamento. Cada ação levará a aplicação web a um estado definido pelo elemento $dSession['etat']['principal']. Este estado destina-se a ser guardado na sessão. Além disso, a ação regista no dicionário $dReponse as informações úteis para a exibição da vista associada ao novo estado em que a aplicação se irá encontrar.

4.10. Os estados da aplicação web

Existem dois:

  • [e-formulaire]: estado em que são apresentadas as diferentes variantes da vista [v-formulaire].
  • [e-erreurs]: estado em que é apresentada a vista [v-erreurs].

As ações autorizadas nestes estados são as seguintes:

<?php

  // configuração dos estados da aplicação
  $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'));        

Num estado, as ações autorizadas correspondem aos destinos URL dos links ou botões [submit] da vista associada ao estado. Além disso, a ação «get:init» está sempre autorizada. Isto permite ao utilizador recuperar o URL main.php da lista de URL do seu navegador e reproduzi-lo novamente, independentemente do estado da aplicação. Trata-se de uma espécie de reinicialização «manual». O estado «sem estado» existe apenas no arranque da aplicação.

A cada estado da aplicação está associado um script responsável por gerar a vista associada a esse estado:

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

O relatório [e-formulaire] apresentará a vista [v-formulaire] com variantes. Com efeito, a vista [v-formulaire] pode ser apresentada em branco, pré-preenchida ou com o montante do imposto. A ação que levará a aplicação ao estado [e-formulaire] especifica, na variável $dSession['etat']['principal'], o estado principal da aplicação. O controlador utiliza apenas esta informação. Na nossa aplicação, a ação que conduz ao estado [e-formulaire] irá adicionar a $dSession['etat']['secondaire'] uma informação complementar que permite ao gerador da resposta saber sedeve gerar um formulário vazio, pré-preenchido, com ou sem o montante do imposto. Poderíamos ter procedido de forma diferente, considerando que existiam aqui três estados diferentes e, portanto, três geradores de visualização a escrever.

4.11. O ficheiro de configuração da aplicação web config.php

<?php

     // configuração do 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 da aplicação
  $dConfig['webapp']=array('titre'=>"Calculez votre impôt");

  // configuração das vistas da aplicação
  $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';  

  // configuração das ações da aplicação
  $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');          

  // configuração dos relatórios da aplicação
  $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'));

  // configuração do modelo da aplicação
    $dConfig["DSN"]=array(
        "sgbd"=>"mysql",
        "user"=>"seldbimpots",
        "mdp"=>"mdpseldbimpots",
        "host"=>"localhost",
        "database"=>"dbimpots"
    );
?>

4.12. As ações da aplicação web

4.12.1. Funcionamento geral dos scripts de ação

  • Um script de ação é chamado pelo controlador de acordo com o parâmetro «action» que este recebeu do cliente.
  • Após a execução, o script de ação deve indicar ao controlador o estado em que a aplicação deve ser colocada. Este estado deve ser indicado em $dSession['etat']['principal'].
  • Um script de ação pode querer inserir informações na sessão. Para tal, insere-as no dicionário $dSession, sendo que este dicionário é automaticamente guardado na sessão pelo controlador no final do ciclo pedido-resposta.
  • Um script de ação pode ter informações a passar para as vistas. Este aspeto é independente do controlador. Trata-se da interface entre as ações e as vistas, uma interface específica de cada aplicação. No exemplo aqui analisado, as ações fornecerão informações aos geradores de vistas através de um dicionário denominado $dReponse.

4.12.2. A ação get:init

É a ação que gera o formulário vazio. O ficheiro de configuração mostra que será processada pelo script a-init.php:

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

O código do script a-init.php é o seguinte:

<?php
     // é apresentado o formulário de introdução de dados
  $dSession['etat']=array('principal'=>'e-formulaire', 'secondaire'=>'init');
?>  

Este script limita-se a definir, em $dSession['etat']['principal'], o estado em que a aplicação deve encontrar-se, o estado [e-formulaire], e fornece em $dSession['etat']['secondaire'] uma especificação sobre esse estado. O ficheiro de configuração mostra-nos que o controlador executará o script e-formulaire.php para gerar a resposta ao cliente.

<?php

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

O script e-formulaire.php irá gerar a vista [v-formulaire] nas suas variantes [init] e c.a.d. O formulário está vazio.

4.12.3. A ação post:calculerimpot

Esta é a ação que permite calcular o imposto com base nos dados introduzidos no formulário. O ficheiro de configuração indica que é o script a-calculimpot.php que irá processar esta ação:

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

O código do script a-calculimpot.php é o seguinte:

<?php
     // pedido de cálculo de impostos

  // verifica-se primeiro a validade dos 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é";
  }

  // se houver erros, o processo termina
  if(count($erreurs)!=0){
      // preparação da página de erros
    $dReponse['erreurs']=&$erreurs;
    $dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'saisie');
      return;
  }//if

  // os dados introduzidos estiverem corretos
  // recuperam-se os dados necessários para o cálculo do imposto
  if(! $dSession['limites']){
      // os dados não se encontram na sessão
    // recuperam-se da fonte de dados
    list($erreurs,$limites,$coeffr,$coeffn)=getData($dConfig['DSN']);
    // se houver erros, é apresentada a página de erros
    if(count($erreurs)!=0){
        // preparação da página de erros
      $dReponse['erreurs']=&$erreurs;
        $dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'database');
      return;
    }//if
    // sem erros — os dados são colocados na sessão
    $dSession['limites']=&$limites;
    $dSession['coeffr']=&$coeffr;
    $dSession['coeffn']=&$coeffn;
  }//se

  // aqui temos os dados necessários para o cálculo do imposto
  // calcula-se o imposto
  $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);

    // preparação da página de resposta
  $dSession['etat']=array('principal'=>'e-formulaire','secondaire'=>'calculimpot');
  $dReponse['impot']=$dPerso['impot'];
  return;

  //-----------------------------------------------------------------------
  function getData($dDSN){
      // ligação à fonte de dados definida pelo dicionário $dDSN
        $oImpots=new impots_data($dDSN);
    if(count($oImpots->aErreurs)!=0) return array($oImpots->aErreurs);
    // recuperação dos dados de limites, coeffr, coeffn
        list($limites,$coeffr,$coeffn)=$oImpots->getData();
         // desligar-se
        $oImpots->disconnect();
    // retornar o resultado
    if(count($oImpots->aErreurs)!=0) return array($oImpots->aErreurs);
        else return array(array(),$limites,$coeffr,$coeffn);
  }//getData

O script faz o que tem de fazer: calcular o imposto. Deixamos ao leitor a tarefa de descodificar o código do processamento. O nosso interesse centra-se nos estados que podem ocorrer na sequência desta ação:

  • os dados introduzidos estão incorretos ou o acesso aos dados falha: a aplicação é colocada no estado [e-erreurs]. O ficheiro de configuração mostra que é o script e-erreurs.php que ficará encarregue de gerar a vista de resposta:
<?php

  $dConfig['etats']['e-erreurs']=array(
      'actionsautorisees'=>array('get:retourformulaire','get:init'),
      'vue'=>'e-erreurs.php');
  • em todos os outros casos, a aplicação é colocada no estado [e-formulaire] com a variante de cálculo de impostos indicada em $dSession['etat']['secondaire']. O ficheiro de configuração indica que é o script e-formulaire.php que irá gerar a vista de resposta. Este irá utilizar o valor de $dSession['etat']['secondaire'] para gerar um formulário pré-preenchido com os valores introduzidos pelo utilizador e, além disso, o montante do imposto.

4.12.4. A ação post:effacerformulaire

Está associada, por configuração, ao script a-init.php já descrito.

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

4.12.5. A ação get:retourformulaire

Permite regressar ao estado [e-formulaire] a partir do estado [e-erreurs]. É o script a-retourformulaire.php que processa esta ação:

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

O script a-retourformulaire.php é o seguinte:

<?php
     // exibe-se o formulário de introdução de dados
  $dSession['etat']=array('principal'=>'e-formulaire','secondaire'=>'retourformulaire');
?>    

Basta solicitar que a aplicação seja colocada no estado [e-formulaire] na sua variante [retourformulaire]. O ficheiro de configuração indica-nos que o controlador executará o script e-formulaire.php para gerar a resposta ao cliente.

<?php

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

O script e-formulaire.php irá gerar a vista [v-formulaire] nas suas variantes [retourformulaire] e c.a.d. o formulário pré-preenchido com os valores introduzidos pelo utilizador, mas sem o montante do imposto.

4.13. A sequência de ações inválida

As ações válidas a partir de um determinado estado da aplicação são definidas por configuração:

<?php

  // configuração dos estados da aplicação
  $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'));

Já explicámos esta configuração. Se for detetada uma sequência inválida de ações, o script a-enchainementinvalide.php é executado:

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

O código deste script é o seguinte:

<?php 
     // sequência de ações inválida
  $dReponse['erreurs']=array("Enchaînement d'actions invalide");
  $dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'enchainementinvalide');  
?>

Consiste em colocar a aplicação no estado [e-erreurs]. Em $dSession['etat']['secondaire'], fornecemos uma informação que será utilizada pelo gerador da página de erros. Como já vimos, esse gerador é o e-erreurs.php:

<?php

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

Veremos o código deste gerador mais tarde. A visualização enviada ao cliente é a seguinte:

Image

4.14. As vistas da aplicação

4.14.1. Exibição da vista final

Vamos ver como o controlador envia a resposta ao cliente, uma vez executada a ação solicitada por este:

<?php

....
  // inicia-se ou retoma-se a sessão
  session_start();
  $dSession=$_SESSION["session"];
  if($dSession) $dSession=unserialize($dSession);

  // recupera-se a ação a realizar
  $sAction=$_GET['action'] ? strtolower($_GET['action']) : 'init';
  $sAction=strtolower($_SERVER['REQUEST_METHOD']).":$sAction";

     // A sequência de ações está normal?
  if( ! enchainementOK($dConfig,$dSession,$sAction)){  
    // sequência anormal
    $sAction='enchainementinvalide';
  }//if

     // processamento da ação
  $scriptAction=$dConfig['actions'][$sAction] ? 
    $dConfig['actions'][$sAction]['url'] : 
    $dConfig['actions']['actionInvalide']['url'];
  include $scriptAction;

  // envio da resposta (visualização) ao cliente
  $sEtat=$dSession['etat']['principal'];
  $scriptVue=$dConfig['etats'][$sEtat]['vue'];
  include $scriptVue;

.....

  // ---------------------------------------------------------------
  function finSession(&$dConfig,&$dReponse,&$dSession){
    // $dConfig: dicionário de configuração
      // $dSession: dicionário contendo as informações da sessão
         // $dReponse: o dicionário de argumentos da página de resposta

    // registo da sessão
...

         // envio da resposta ao cliente
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    // fim do script
    exit(0);
  }//fim da sessão      

Após o retorno de um script de ação, o controlador recupera em $dSession['etat']['principal'] o estado em que deve colocar a aplicação. Este estado foi definido pela ação que acabou de ser executada. O controlador faz então com que seja executado o gerador de visualização associado ao estado. Encontra o nome deste no ficheiro de configuração. A função do gerador de visualização é a seguinte:

  • define em $dReponse['vuereponse'] o nome do modelo de resposta a utilizar. Esta informação será passada ao controlador. Um modelo é uma composição de vistas elementares que, quando reunidas, formam a vista final.
  • prepara as informações dinâmicas a apresentar na vista final. Este ponto é independente do controlador. Trata-se da interface entre o gerador de vistas e a vista final. É específica de cada aplicação.
  • termina obrigatoriamente com a chamada à função finSession do controlador. Esta função irá
    • guardar a sessão
    • enviar a resposta

O código da função finSession é o seguinte:

<?php

  // ---------------------------------------------------------------
  function finSession(&$dConfig,&$dReponse,&$dSession){
    // $dConfig: dicionário de configuração
      // $dSession: dicionário contendo as informações da sessão
         // $dReponse: o dicionário de argumentos da página de resposta

    // registo da sessão
...

         //: envio da resposta ao cliente
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    // fim do script
    exit(0);
  }//fim da sessão      

A visualização enviada ao utilizador é definida pela entidade $dReponse['vuereponse'], que define o modelo a utilizar para a resposta final.

4.14.2. Modelo da resposta

A aplicação irá gerar as suas diferentes respostas de acordo com o seguinte modelo único:

Este modelo está associado à chave «modéle1» do dicionário $dConfig['vuesreponse']:

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

O script m-reponse.php é responsável por gerar este modelo:

<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 tem três elementos dinâmicos colocados num dicionário $dReponse e associados às seguintes chaves:

  • urlstyle: URL da folha de estilo do modelo
  • vue1: nome do script responsável por gerar a vista vue1
  • vue2: nome do script responsável por gerar a vista vue2

Um gerador de vista que pretenda utilizar o modelo «modelo1» deverá definir estes três elementos dinâmicos. Definimos agora as vistas elementares que podem substituir os elementos [vue1] e [vue2] do modelo.

4.14.3. A vista elementar v-bandeau.php

O script v-bandeau.php gera uma vista que será colocada na 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>    

O gerador de visualização deverá definir dois elementos dinâmicos colocados num dicionário $dReponse e associados às seguintes chaves:

  • título: título a apresentar
  • resultado: montante do imposto a pagar

4.14.4. A vista elementar v-formulaire.php

A parte [vue2] corresponde à vista [v-formulaire] ou à vista [v-erreurs]. A vista [v-formulaire] é gerada pelo 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>                                            

As partes dinâmicas desta vista, que deverão ser definidas pelo gerador de vistas, estão associadas às seguintes chaves do dicionário $dReponse:

  • optoui: estado do botão de opção com o nome optoui
  • optnon: estado do botão de opção com o nome optnon
  • enfants: número de filhos a inserir no campo txtenfants
  • salário: salário anual a inserir no campo txtsalário

4.14.5. A vista elementar v-erreurs.php

A vista [v-erreurs] é gerada pelo 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>

As partes dinâmicas desta vista, a definir pelo gerador de vistas, estão associadas às seguintes chaves do dicionário $dReponse:

  • erros: tabela de mensagens de erro
  • info: mensagem informativa
  • ligação: texto de uma ligação
  • href: URL de destino do link acima

4.14.6. A folha de estilo

Todas as vistas são «formatas» por uma folha de estilo. Para alterar o aspeto visual da aplicação, deve-se modificar a sua folha de estilo. A seguinte folha 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. Os geradores de vistas

4.15.1. Função de um gerador de vistas

Recorde-se a sequência de código do controlador que executa um gerador de visualização:

<?php

     // processamento da ação
  $scriptAction=$dConfig['actions'][$sAction] ? 
    $dConfig['actions'][$sAction]['url'] : 
    $dConfig['actions']['actionInvalide']['url'];
  include $scriptAction;

  // envio da resposta (visualização) ao cliente
  $sEtat=$dSession['etat']['principal'];
  $scriptVue=$dConfig['etats'][$sEtat]['vue'];
  include $scriptVue;

Um gerador de vista está associado ao estado no qual a aplicação será colocada. A ligação entre o estado e o gerador de vista é definida por configuração:

<?php

  // configuração dos estados da aplicação
  $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'));

Já indicámos qual era a função de um gerador de visualização. Vamos relembrá-la aqui. Um gerador de visualização:

  • define em $dReponse['vuereponse'] o nome do modelo de resposta a utilizar. Esta informação será passada ao controlador. Um modelo é uma composição de vistas elementares que, quando reunidas, formam a vista final.
  • prepara as informações dinâmicas a apresentar na vista final. Este aspeto é independente do controlador. Trata-se da interface entre o gerador de vistas e a vista final. É específica de cada aplicação.
  • termina obrigatoriamente com a chamada à função finSession do controlador. Esta função irá
    • guardar a sessão
    • enviar a resposta

4.15.2. O gerador de visualização associado ao estado [e-formulaire]

O script responsável por gerar a vista associada ao estado [e-formulaire] chama-se e-formulaire.php:

<?php

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

O seu código é o seguinte:

<?php
  // prepara-se a resposta do formulário
  $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'];

  // configuração de acordo com o tipo de formulário a gerar
    $type=$dSession['etat']['secondaire'];
  if($type=='init'){
      // formulário em branco
    $dReponse['optnon']='checked';
  }//if
  if($type=='calculimpot'){
      // é necessário voltar a apresentar os parâmetros de introdução de dados armazenados na 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'){
      // é necessário voltar a apresentar os parâmetros de introdução de dados armazenados na sessão
    $dReponse['optoui']=$dSession['requete']['optmarie']=='oui' ? 'checked' : '';
    $dReponse['optnon']=$dReponse['optoui']=='' ? 'checked' : '';  
    $dReponse['enfants']=$dSession['requete']['txtenfants'];
    $dReponse['salaire']=$dSession['requete']['txtsalaire'];
  }//if
  // envia-se a resposta
  finSession($dConfig,$dReponse,$dSession);
?>      

Note-se que o gerador de visualizações cumpre as condições impostas a um gerador de visualizações:

  • definir em $dReponse['vuereponse'] o modelo de resposta a utilizar
  • passar informações para esse modelo. Neste caso, estas são passadas através do dicionário $dReponse.
  • terminar com a chamada à função finSession do controlador

Aqui, o modelo utilizado é «modelo1». Assim, o gerador define as duas informações de que este modelo necessita: $dReponse['vue1'] e $dReponse['vue2'].

No caso específico da nossa aplicação, a vista associada ao estado [e-formulaire] depende de uma informação armazenada na variável $dSession['etat']['secondaire']. Trata-se de uma escolha de desenvolvimento. Outra aplicação poderia optar por transmitir informações complementares de outra forma. Além disso, neste caso, todas as informações necessárias para a exibição da vista final são colocadas no dicionário $dReponse. Mais uma vez, trata-se de uma escolha que cabe ao programador. O estado [e-formulaire] pode ocorrer após quatro ações diferentes: init, calcularimposto, regressar ao formulário, apagar o formulário. A vista a apresentar não é exatamente a mesma em todos os casos. Por isso, distinguiram-se aqui três casos em $dSession['etat']['secondaire']:

  • init: o formulário é apresentado vazio
  • calculo do imposto: o formulário é apresentado com o montante do imposto e os dados que conduziram ao seu cálculo
  • retourformulaire: o formulário é apresentado com os dados inicialmente introduzidos

Acima, o script e-formulaire.php utiliza esta informação para apresentar a resposta de acordo com estas três variantes.

4.15.3. A vista associada ao relatório [e-erreurs]

O script responsável por gerar a vista associada ao relatório [e-erreurs] chama-se e-erreurs.php e é o seguinte:

<?php

  // preparamos a resposta de erros
  $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';

  // informações adicionais
  $type=$dSession['etat']['secondaire'];
  if($type=='database'){
      $dReponse['info']="Veuillez avertir l'administrateur de l'application";
  }

  // envia-se a resposta
  finSession($dConfig,$dReponse,$dSession);
?>

4.15.4. Exibição da vista final

Os dois scripts que geram as duas vistas finais terminam ambos com a chamada à função finSession do controlador:

<?php

  function finSession(&$dConfig,&$dReponse,&$dSession){
    // $dConfig: dicionário de configuração
      // $dSession: dicionário contendo as informações da sessão
         // $dReponse: o dicionário de argumentos da página de resposta

....

         // apresenta a resposta
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    // fim do script
    exit(0);
  }//fim da sessão      

A visualização enviada ao utilizador é definida pela entidade $dReponse['vuereponse'], que define o modelo a utilizar para a resposta final. Para os relatórios [e-formulaire] e [e-erreurs], este modelo foi definido como «modelo1»:

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

Este modelo corresponde, por configuração, ao script m-reponse.php:

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

4.16. Alterar o modelo de resposta

Partimos do princípio de que se decide alterar o aspeto visual da resposta enviada ao cliente e pretendemos compreender as repercussões que isso acarreta ao nível do código.

4.16.1. O novo modelo

A estrutura da resposta será agora a seguinte:

Este modelo será denominado «modele2» e o script responsável por gerar este modelo será denominado «m-reponse2.php»:

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

O script correspondente a este modelo é o seguinte:

<html>
    <head>
      <title>Application impots</title>
      <link type="text/css" href="<?php echo $dReponse['urlstyle'] ?>" rel="stylesheet" />
  </head>
  <body>
      <table class='modele2'>
        <!-- início do banner -->
        <tr>
          <td colspan="2"><?php    include $dReponse['vue1']; ?></td>
      </tr>
        <!-- fim do banner -->            
      <tr>
            <!-- início do menu -->      
          <td><?php include $dReponse['vue2']; ?></td>
            <!-- fim do menu -->
            <!-- início da zona 3 -->                        
          <td><?php include $dReponse['vue3']; ?></td>
            <!-- fim da zona 3 -->        
      </tr>
   </table>
    </body>
</html>

Os elementos dinâmicos do modelo são os seguintes:

  • $dReponse['urlstyle']: a folha de estilo a utilizar
  • $dReponse['vue1']: o script a utilizar para gerar [vue1]
  • $dReponse['vue2']: o script a utilizar para gerar [vue2]
  • $dReponse['vue3']: o script a utilizar para gerar [vue3]

Estes elementos deverão ser definidos pelos criadores das visualizações.

4.16.2. As diferentes páginas de resposta

A aplicação apresentará agora as seguintes respostas ao utilizador. Na chamada inicial, a página de resposta será a seguinte:

Se o utilizador fornecer dados válidos, o imposto é calculado:

Se cometer erros na introdução de dados, é apresentada a página de erros:

Se utilizar o link [Retour au formulaire de saisie], volta a aceder ao formulário tal como o validou:

Se, no exemplo acima, utilizar o link [Réinitialiser le formulaire], irá encontrar um formulário em branco:

Note-se que a aplicação utiliza, de facto, as mesmas ações que anteriormente. Apenas o aspeto das respostas mudou.

4.16.3. As vistas elementares

A vista elementar [vue1] será, tal como no exemplo anterior, associada ao 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 tem dois elementos dinâmicos:

  • $dReponse['titre']: título a apresentar
  • $dReponse['resultat']: montante do imposto a pagar

A vista elementar [vue2] será associada ao seguinte 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'].
        '">'.$dRespostaQZXW2HTMLBWydsaWVucyddZQXQZXW2HTMLBWyRpXQZQXQZXW2HTMLBWyd0ZXh0ZSddZQX."</a></div></td></tr>\n";
    }//$i
  ?>
</table>

Esta vista contém os seguintes elementos dinâmicos:

  • $dReponse['liens']: tabela de links a apresentar em [vue2]. Cada elemento da tabela é um dicionário com duas chaves:
    • 'url': URL de destino do link
    • 'texto': texto do link

A vista elementar [vue3] será associada ao script v-formulaire2.php se se pretender apresentar o formulário de introdução de dados, ou ao script v-erreurs2.php se se pretender apresentar a página de erros. O código do script v-formulaire2.php é o seguinte:

<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>

As partes dinâmicas desta vista, que deverão ser definidas pelo gerador de vistas, estão associadas às seguintes chaves do dicionário $dReponse:

  • optoui: estado do botão de opção com o nome optoui
  • optnon: estado do botão de opção com o nome optnon
  • enfants: número de filhos a inserir no campo txtenfants
  • salário: salário anual a inserir no campo txtsalário

O script que gera a página de erros chama-se v-erreurs2.php. O seu código é o seguinte:

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";
        }//para
    ?>
</ul>
<div class="info"><?php echo $dReponse["info"] ?></div>

As partes dinâmicas desta vista, a definir pelo gerador de vistas, estão associadas às seguintes chaves do dicionário $dReponse:

  • erros: tabela de mensagens de erro
  • info: mensagem informativa

4.16.4. A folha de estilo

Não sofreu alterações. Continua a ser style1.css.

4.16.5. O novo ficheiro de configuração

Para introduzir estas novas vistas, temos de alterar algumas linhas do ficheiro de configuração.

<?php

     // configuração do 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 da aplicação
  $dConfig['webapp']=array('titre'=>"Calculez votre impôt");

  // configuração das vistas da aplicação
  $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';  

  // configuração das ações da aplicação
  $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');          

  // configuração dos relatórios da aplicação
  $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'));

  // configuração do modelo da aplicação
    $dConfig["DSN"]=array(
        "sgbd"=>"mysql",
        "user"=>"seldbimpots",
        "mdp"=>"mdpseldbimpots",
        "host"=>"localhost",
        "database"=>"dbimpots"
    );
?>

A alteração essencial consiste em substituir os geradores de visualização associados aos relatórios [e-formulaire] e [e-erreurs]. Feito isto, os novos geradores de visualização ficam encarregues de gerar as novas páginas de resposta.

4.16.6. O gerador de visualização associado ao relatório [e-formulaire]

No ficheiro de configuração, o estado [e-formulaire] está agora associado ao gerador de visualização e-formulaire2.php. O código deste script é o seguinte:

<?php
  // prepara-se a resposta do formulário
  $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')
  );              

  // configuração de acordo com o tipo de formulário a gerar
    $type=$dSession['etat']['secondaire'];
  if($type=='init'){
      // formulário vazio
    $dReponse['optnon']='checked';
  }//if
  if($type=='calculimpot'){
      // é necessário voltar a apresentar os parâmetros de introdução de dados armazenados na 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'){
      // é necessário voltar a apresentar os parâmetros de entrada armazenados na sessão
    $dReponse['optoui']=$dSession['requete']['optmarie']=='oui' ? 'checked' : '';
    $dReponse['optnon']=$dReponse['optoui']=='' ? 'checked' : '';  
    $dReponse['enfants']=$dSession['requete']['txtenfants'];
    $dReponse['salaire']=$dSession['requete']['txtsalaire'];
  }//if
  // envia-se a resposta
  finSession($dConfig,$dReponse,$dSession);
?>  

As principais alterações são as seguintes:

  • o gerador de visualizações indica que pretende utilizar o modelo de resposta «modele2»
  • por esse motivo, preenche os elementos dinâmicos $dReponse['vue1'], $dReponse['vue2'], $dReponse['vue3'], sendo os três necessários para o modelo de resposta «modele2».
  • O gerador preenche também o elemento dinâmico $dReponse['liens'], que define os links a apresentar na área [vue2] da resposta.

4.16.7. O gerador de visualização associado ao relatório [e-erreurs]

No ficheiro de configuração, o relatório [e-erreurs] está agora associado ao gerador de visualização e-erreurs2.php. O código deste script é o seguinte:

<?php

  // preparamos a resposta de erros
  $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')
  );              

  // informações adicionais
  $type=$dSession['etat']['secondaire'];
  if($type=='database'){
      $dReponse['info']="Veuillez avertir l'administrateur de l'application";
  }

  // envia-se a resposta
  finSession($dConfig,$dReponse,$dSession);
?>  

As alterações introduzidas são idênticas às introduzidas no gerador de visualização e-formulaire2.php.

4.17. Conclusão

Conseguimos demonstrar, através de um exemplo, a vantagem do nosso controlador genérico. Não foi necessário escrevê-lo. Limitámo-nos a escrever os scripts das ações, dos geradores de visualização e das visualizações da aplicação. Além disso, demonstrámos a vantagem de separar as ações das visualizações. Assim, conseguimos alterar o aspeto das respostas sem modificar uma única linha de código dos scripts de ação. Apenas os scripts envolvidos na geração das vistas foram alterados. Para que isto seja possível, o script de ação não deve fazer qualquer suposição sobre a vista que irá apresentar as informações que calculou. Deve limitar-se a entregar essas informações ao controlador, que as transmite ao gerador de vistas, o qual lhes dará formato. Esta é uma regra absoluta: uma ação deve estar completamente separada das vistas.

Neste capítulo, abordámos a filosofia Struts, bem conhecida dos programadores Java. Um projeto «open source» denominado php.mvc permite realizar desenvolvimento web/PHP com a filosofia Struts. Consulte o site http://www.phpmvc.net/ para mais informações.