Skip to content

4. Um exemplo prático

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 seu salário para declarar:

  • calcula-se o número de cotas do empregado nbParts = nbEnfants/2 + 1 se ele for solteiro, nbEnfants/2 + 2 se for casado, onde nbEnfants é o número de filhos. O número de faixas é aumentado em 0,5 se houver três filhos ou mais.
  • calcula-se sua renda tributável R = 0,72 * S, onde S é seu salário anual
  • calcula-se seu coeficiente familiar Q = R/N
  • calcula-se seu imposto I com base nos seguintes dados
limite
coeffR
coeffN
12620,0
0
0
13190
0,05
631
15.640
0,1
1.290,5
24.740
0,15
2.072,5
31.810
0,2
3.309,5
39.970
0,25
4.900
48.360
0,3
6.898,5
55.790
0,35
9.316,5
92.970
0,4
12.106
127.860
0,45
16.754,5
151.250
0,50
23.147,5
172040
0,55
30710
195.000
0,60
39.312
0
0,65
49.062
Cada linha possui três 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 resulta no imposto I = 0,65*R - 49062*nbParts.

4.2. O banco de dados

Os dados anteriores são registrados em um banco de dados MySQL chamado dbimpots. O usuário seldbimpots, com a senha mdpseldbimpots, tem acesso somente para leitura ao conteúdo do banco de dados. Este possui uma única tabela chamada impots, cuja estrutura é a seguinte:

Image

Seu conteúdo é o seguinte:

Image

4.3. A arquitetura MVC do aplicativo

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

  • o controlador main.php será o controlador genérico apresentado anteriormente
  • a solicitação do cliente é enviada ao controlador na forma de uma requisiçã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 retorna ao controlador uma variável indicando o estado em que a aplicação web deve ser colocada. Com esse estado, o controlador ativará um dos geradores de visualizações para enviar a resposta ao cliente.
  • impots-data.php é a classe responsável por fornecer ao controlador os dados de que ele necessita
  • impots-calcul.php é a classe de negócios que permite o cálculo do imposto

4.4. A classe de acesso aos dados

A classe de acesso aos dados foi criada para ocultar da aplicação web a origem dos dados. Em sua interface, encontramos um método getData que fornece os três tabuletos de dados necessários para o cálculo do imposto. Em nosso exemplo, os dados são extraídos de um banco 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 conexão
      var $sDatabase;            // nome do banco de dados
    var $oDB;                        // conexão com o banco de dados
    var $aErreurs;            // lista de erros
    var $oRésultats;        // resultado de uma consulta
        var $connecté;            // valor booleano que indica se há ou não conexão com o banco de dados
        var $sQuery;                // a última consulta executada

    // construtor
    function impots_data($dDSN){

        // $dDSN: dicionário que define a conexão a ser estabelecida
      // $dDSN['sgbd']: o tipo do SGBD ao qual é necessário se conectar
      // $dDSN['host']: o nome do servidor que o hospeda      
      // $dDSN['database']: o nome do banco de dados ao qual é necessário se conectar      
      // $dDSN['user']: um usuário do banco de dados
      // $dDSN['mdp']: sua senha

      // cria em $oDB uma conexão com o banco de dados definido por $dDSN sob a identidade de $dDSN['user']
      // se a conexão for bem-sucedida  
          // insere em $sDSN a string de conexão com o banco de dados
          // armazena em $sDataBase o nome do banco de dados ao qual se está conectando
        // define $connecté como verdadeiro
      // se a conexão falhar
          // insere as mensagens de erro adequadas na lista $aErreurs
        // encerra a conexão, se necessário
        // define $connecté como falso 

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

      // cria-se uma conexão com o banco de dados $sDSN
      $this->sDSN=$dDSN["sgbd"]."://".$dDSN["user"].":".$dDSN["mdp"]."@".$dDSN["host"]."/".$dDSN["database"];
      $this->sDatabase=$dDSN["database"];
      $this->connect();

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

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

    // ------------------------------------------------------------------
    function connect(){
        // (re)conexão com o banco de dados
      // limpar lista de erros
            $this->aErreurs=array();

      // estamos criando uma conexão com o banco de dados $sDSN
        $this->oDB=DB::connect($this->sDSN,true);

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

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

    // ------------------------------------------------------------------
    function disconnect(){
      // se estiver conectado, encerra-se a conexão com a base $sDSN
        if($this->connecté){
              $this->oDB->disconnect();
          // estamos desconectados
        $this->connecté=FALSE;
            }//se
    }//desconectar

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

        // a consulta está sendo armazenada
            $this->sQuery=$sQuery;    

      // estamos conectados?
      if(! $this->connecté){
          // registrando 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)){
            // registra-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 da 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

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

  // ----------------------------------
  function checkErreurs(&$oImpots){
      // erros?
    if(count($oImpots->aErreurs)!=0){
        // exibição
      for($i=0;$i<count($oImpots->aErreurs);$i++){
          echo $oImpots->aErreurs[$i]."\n";
      }//para
      // erros
      return true;
    }//se
    // 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

Essa classe permite calcular o imposto de um contribuinte. São fornecidos ao seu construtor os dados necessários para esse cálculo. Em seguida, ele se encarrega 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 indicando 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 renda
      // coeficientes: tabela dos coeficientes de renda
      // coeffn: tabela dos coeficientes do número de cotas
      // as três tabelas têm o mesmo número de elementos

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

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

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

      // busca 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 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)){
    exit(0);
  }
  // desconectando
  $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){
        // exibição
      for($i=0;$i<count($oImpots->aErreurs);$i++){
          echo $oImpots->aErreurs[$i]."\n";
      }//para
      // erros
      return true;
    }//se
    // 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 do aplicativo

Ao iniciar o aplicativo web de cálculo de impostos, é exibida a seguinte tela [v-formulaire]:

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

Observe-se que o formulário é regenerado no estado em que o usuário o validou e que, além disso, exibe o valor do imposto a pagar. O usuário pode cometer erros de digitação. Esses erros são sinalizados por meio de uma página de erros, que chamaremos de tela [v-erreurs].

O link [Retour au formulaire de saisie] permite que o usuário recupere o formulário exatamente como o validou.

Por fim, o botão [Effacer le formulaire] reverte o formulário ao seu estado inicial, c.a.d, tal como o usuário o recebeu na solicitação inicial.

4.7. Análise da arquitetura MVC do aplicativo

A aplicação possui a seguinte arquitetura MVC:

Acabamos de descrever as duas classes impots-data.php e impots-calcul.php. Passaremos agora a descrever os demais elementos da arquitetura.

4.8. O controlador da aplicação

O controlador main.php da aplicação é aquele 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 ser realizada
  $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 deveríamos chegar até 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

    // registro da sessão
    if(isset($dSession)){
      //: os parâmetros da solicitação são inseridos 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 do aplicativo web

Existem quatro ações:

  • get:init: é a ação acionada durante a solicitação inicial sem parâmetros ao controlador. Ela gera a visualização “formulário” vazia.
  • post:effacerformulaire: ação acionada pelo botão [Effacer le formulaire]. Ela gera a visualização [v-formulaire] vazia.
  • post:calculerimpot: ação acionada pelo botão [Calculer l'impôt]. Ela gera a visualização [v-formulaire] com o valor do imposto a pagar ou a visualização [v-erreurs].
  • get:retourformulaire: ação acionada pelo link [Retour au formulaire de saisie]. Ela gera a visualização [v-formulaire] pré-preenchida com os dados incorretos.

Essas ações são configuradas da seguinte maneira no arquivo de configuração:

<?php

  // configuração das ações do aplicativo
  $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 por processá-la. Cada ação levará o aplicativo web a um estado definido pelo elemento $dSession['etat']['principal']. Esse estado deve ser salvo na sessão. Além disso, a ação registra no dicionário $dReponse as informações necessárias para a exibição da visualização associada ao novo estado em que a aplicação se encontrará.

4.10. Os relatórios do aplicativo web

Existem dois:

  • [e-formulaire]: relatório no qual são apresentadas as diferentes variantes da visualização [v-formulaire].
  • [e-erreurs]: estado em que é apresentada a visualização [v-erreurs].

As ações permitidas nesses estados são as seguintes:

<?php

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

Em um estado, as ações autorizadas correspondem aos destinos URL dos links ou botões [submit] da visualização associada ao estado. Além disso, a ação 'get:init' está sempre autorizada. Isso permite que o usuário recupere o URL main.php da lista de URL do seu navegador e o reproduza novamente, independentemente do estado do aplicativo. Trata-se de uma espécie de reinicialização “manual”. O estado “sem estado” existe apenas na inicialização do aplicativo.

A cada estado do aplicativo está associado um script responsável por gerar a visualização correspondente ao estado:

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

O relatório [e-formulaire] apresentará a visualização [v-formulaire] com variantes. De fato, a visualização [v-formulaire] pode ser apresentada vazia, pré-preenchida ou com o valor do imposto. A ação que levará o aplicativo ao relatório [e-formulaire] especifica, na variável $dSession['etat']['principal'], o relatório principal do aplicativo. O controlador utiliza apenas essa informação. Em nosso aplicativo, a ação que leva ao estado [e-formulaire] adicionará em $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 valor do imposto. Poderíamos ter procedido de maneira diferente, considerando que haviam ali três estados diferentes e, portanto, três geradores de visualização a serem escritos.

4.11. O arquivo 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 serem incluídos
  $dConfig['includes']=array('c-impots-data.php','c-impots-calcul.php');

  // controlador do aplicativo
  $dConfig['webapp']=array('titre'=>"Calculez votre impôt");

  // configuração das visualizações 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 do aplicativo
  $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 do aplicativo
  $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 do aplicativo 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. Esse estado deve ser indicado em $dSession['etat']['principal'].
  • Um script de ação pode precisar inserir informações na sessão. Para isso, ele as insere no dicionário $dSession, sendo que esse dicionário é automaticamente salvo na sessão pelo controlador ao final do ciclo de solicitação-resposta.
  • Um script de ação pode ter informações a serem passadas para as visualizações. Esse aspecto é independente do controlador. Trata-se da interface entre as ações e as visualizações, interface específica de cada aplicação. No exemplo analisado aqui, as ações fornecerão informações aos geradores de visualizações por meio de um dicionário chamado $dReponse.

4.12.2. A ação get:init

Essa é a ação que gera o formulário vazio. O arquivo de configuração mostra que ela 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
     // exibe-se o formulário de preenchimento
  $dSession['etat']=array('principal'=>'e-formulaire', 'secondaire'=>'init');
?>  

Este script se limita a definir, em $dSession['etat']['principal'], o estado em que o aplicativo deve se encontrar, o estado [e-formulaire], e fornece em $dSession['etat']['secondaire'] uma especificação sobre esse estado. O arquivo de configuração nos mostra 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 gerará a visualização [v-formulaire] em suas variantes [init] e c.a.d. O formulário estará vazio.

4.12.3. A ação post:calculerimpot

Essa é a ação que permite calcular o imposto a partir dos dados inseridos no formulário. O arquivo de configuração indica que é o script a-calculimpot.php que processará essa ação:

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

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

<?php
     // solicitação de cálculo de imposto

  // primeiro verifica-se 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 é encerrado
  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 inseridos estiverem corretos
  // recuperam-se os dados necessários para o cálculo do imposto
  if(! $dSession['limites']){
      // os dados não estão na sessão
    // eles são recuperados da fonte de dados
    list($erreurs,$limites,$coeffr,$coeffn)=getData($dConfig['DSN']);
    // se houver erros, exibe-se 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 inseridos 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
  // calculamos 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){
      // conexão com a 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();
         // desconexão
        $oImpots->disconnect();
    // retornando o resultado
    if(count($oImpots->aErreurs)!=0) return array($oImpots->aErreurs);
        else return array(array(),$limites,$coeffr,$coeffn);
  }//getData

O script faz o que deve fazer: calcular o imposto. Deixamos a cargo do leitor a tarefa de interpretar o código do processamento. Nosso foco está nos estados que podem ocorrer em decorrência dessa ação:

  • os dados inseridos estão incorretos ou o acesso aos dados falha: o aplicativo é colocado no estado [e-erreurs]. O arquivo de configuração mostra que é o script e-erreurs.php que será responsável por gerar a visualização de resposta:
<?php

  $dConfig['etats']['e-erreurs']=array(
      'actionsautorisees'=>array('get:retourformulaire','get:init'),
      'vue'=>'e-erreurs.php');
  • em todos os outros casos, o aplicativo é colocado no estado [e-formulaire] com a variante de cálculo de impostos indicada em $dSession['etat']['secondaire']. O arquivo de configuração mostra que é o script e-formulaire.php que irá gerar a visualização da resposta. Ele utilizará o valor de $dSession['etat']['secondaire'] para gerar um formulário pré-preenchido com os valores inseridos pelo usuário e, além disso, o valor do imposto.

4.12.4. A ação post:effacerformulaire

Ela 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

Ela permite retornar ao estado [e-formulaire] a partir do estado [e-erreurs]. O script a-retourformulaire.php é responsável por processar essa 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 entrada
  $dSession['etat']=array('principal'=>'e-formulaire','secondaire'=>'retourformulaire');
?>    

Basta solicitar que o aplicativo seja colocado no estado [e-formulaire], em sua variante [retourformulaire]. O arquivo de configuração nos mostra 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 gerará a visualização [v-formulaire] em suas variantes [retourformulaire] e c.a.d. o formulário pré-preenchido com os valores inseridos pelo usuário, mas sem o valor do imposto.

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

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

<?php

  // configuração dos estados do aplicativo
  $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á explicamos essa configuração. Se for detectada 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 desse 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 o aplicativo 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 desse gerador mais adiante. A visualização enviada ao cliente é a seguinte:

Image

4.14. As visualizações do aplicativo

4.14.1. Exibição da visualização final

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

<?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 ser realizada
  $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

    // registro da sessão
...

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

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

Ao retornar de um script de ação, o controlador recupera em $dSession['etat']['principal'] o estado no qual deve colocar o aplicativo. Esse estado foi definido pela ação que acabou de ser executada. O controlador então executa o gerador de visualização associado ao estado. Ele encontra o nome deste no arquivo de configuração. A função do gerador de visualização é a seguinte:

  • define em $dReponse['vuereponse'] o nome do modelo de resposta a ser utilizado. Essa informação será passada ao controlador. Um modelo é uma composição de visões elementares que, juntas, formam a visão final.
  • prepara as informações dinâmicas a serem exibidas na visualização final. Esse ponto é independente do controlador. Trata-se da interface entre o gerador de visualizações e a visualização final. Ela é específica para cada aplicação.
  • deve terminar obrigatoriamente com a chamada à função finSession do controlador. Essa função irá
    • salvar 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

    // registro 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 usuário é definida pela entidade $dReponse['vuereponse'], que define o modelo a ser utilizado para a resposta final.

4.14.2. Modelo da resposta

O aplicativo gerará suas diferentes respostas de acordo com o seguinte modelo único:

Este modelo está associado à chave “modelo1” 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 possui três elementos dinâmicos inseridos em um 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 visualização vue1
  • vue2: nome do script responsável por gerar a visualização vue2

Um gerador de visualização que deseje utilizar o modelo modelo1 deverá definir esses três elementos dinâmicos. Definiremos agora as visualizações 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 área [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 inseridos em um dicionário $dReponse e associados às seguintes chaves:

  • título: título a ser exibido
  • resultado: valor do imposto a pagar

4.14.4. A visualização elementar v-formulaire.php

A parte [vue2] corresponde à visualização [v-formulaire] ou à visualização [v-erreurs]. A visão [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 visualização, que deverão ser definidas pelo gerador de visualização, estão associadas às seguintes chaves do dicionário $dReponse:

  • optoui: estado do botão de opção chamado optoui
  • optnon: estado do botão de opção chamado optnon
  • filhos: número de filhos a ser inserido no campo txtenfants
  • salário: salário anual a ser inserido no campo txtsalário

4.14.5. A visualização elementar v-erreurs.php

A visualização [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 visão, a serem definidas pelo gerador de visões, estão associadas às seguintes chaves do dicionário $dReponse:

  • erros: tabela de mensagens de erro
  • info: mensagem informativa
  • link: texto de um link
  • href: URL de destino do link acima

4.14.6. A folha de estilo

Todas as visualizações são “estilizadas” por uma folha de estilo. Para alterar a aparência visual do aplicativo, é preciso modificar 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. Geradores de visualizações

4.15.1. Função de um gerador de visualização

Vamos relembrar 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 visualização está vinculado ao estado no qual o aplicativo será colocado. A ligação entre o estado e o gerador de visualização é definida por meio da configuração:

<?php

  // configuração dos estados do aplicativo
  $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á explicamos qual é 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 ser utilizado. Essa informação será passada ao controlador. Um modelo é uma composição de visões elementares que, juntas, formam a visão final.
  • prepara as informações dinâmicas a serem exibidas na visualização final. Esse ponto é independente do controlador. Trata-se da interface entre o gerador de visualizações e a visualização final. Ela é específica para cada aplicação.
  • deve terminar obrigatoriamente com a chamada à função finSession do controlador. Essa função irá
    • salvar a sessão
    • enviar a resposta

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

O script responsável por gerar a visualização associada ao relatório [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');

Seu código é o seguinte:

<?php
  // preparação da 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 ser gerado
    $type=$dSession['etat']['secondaire'];
  if($type=='init'){
      // formulário vazio
    $dReponse['optnon']='checked';
  }//if
  if($type=='calculimpot'){
      // é necessário exibir novamente os parâmetros de entrada 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'){
      // precisamos exibir novamente 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
  // enviamos a resposta
  finSession($dConfig,$dReponse,$dSession);
?>      

Observe-se que o gerador de visualização cumpre as condições exigidas para um gerador de visualização:

  • definir em $dReponse['vuereponse'] o modelo de resposta a ser utilizado
  • passar informações para esse modelo. Aqui, elas são passadas por meio 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 necessárias para esse modelo: $dReponse['vue1'] e $dReponse['vue2'].

No caso específico de nossa aplicação, a visualização 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 maneira. Além disso, aqui todas as informações necessárias para a exibição da visualização final são colocadas no dicionário $dReponse. Mais uma vez, essa é uma escolha que cabe ao desenvolvedor. O estado [e-formulaire] pode ocorrer após quatro ações diferentes: init, calcularimposto, retornarformulário, apagarformulário. A visualização a ser exibida não é exatamente a mesma em todos os casos. Por isso, distinguimos aqui três casos em $dSession['etat']['secondaire']:

  • init: o formulário é exibido vazio
  • cálculo do imposto: o formulário é exibido com o valor do imposto e os dados que levaram ao seu cálculo
  • retourformulaire: o formulário é exibido com os dados inseridos inicialmente

Acima, o script e-formulaire.php utiliza essas informações para apresentar a resposta de acordo com essas três variantes.

4.15.3. A visualização associada ao relatório [e-erreurs]

O script responsável por gerar a visualização 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 complementares
  $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 visualização final

Os dois scripts que geram as duas visualizações finais terminam 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 usuário é definida pela entidade $dReponse['vuereponse'], que define o modelo a ser utilizado para a resposta final. Para os relatórios [e-formulaire] e [e-erreurs], esse modelo foi definido como igual a modele1:

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

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

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

4.16. Modificar o modelo de resposta

Supomos aqui que se decida alterar a aparência visual da resposta enviada ao cliente e nos interessamos em compreender as repercussões que isso acarreta no código.

4.16.1. O novo modelo

A estrutura da resposta será agora a seguinte:

Esse modelo será chamado de modele2 e o script responsável por gerá-lo será chamado de 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 ser utilizada
  • $dReponse['vue1']: o script a ser utilizado para gerar [vue1]
  • $dReponse['vue2']: o script a ser usado para gerar [vue2]
  • $dReponse['vue3']: o script a ser utilizado para gerar [vue3]

Esses elementos deverão ser definidos pelos geradores de visualizações.

4.16.2. As diferentes páginas de resposta

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

Se o usuário fornecer dados válidos, o imposto será calculado:

Caso cometa erros na digitação, a página de erros será exibida:

Se ele utilizar o link [Retour au formulaire de saisie], ele voltará ao formulário exatamente como o validou:

Se, no exemplo acima, ele usar o link [Réinitialiser le formulaire], ele encontrará um formulário vazio:

Observe-se que o aplicativo utiliza, de fato, as mesmas ações de antes. Apenas a aparência das respostas mudou.

4.16.3. As visualizações básicas

A visualização elementar [vue1] será, assim 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 visualização possui dois elementos dinâmicos:

  • $dReponse['titre']: título a ser exibido
  • $dReponse['resultat']: valor do imposto a pagar

A visualização 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 visualização possui os seguintes elementos dinâmicos:

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

A visualização elementar [vue3] será associada ao script v-formulaire2.php se quisermos exibir o formulário de preenchimento ou ao script v-erreurs2.php se quisermos exibir 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 visualização, que deverão ser definidas pelo gerador de visualização, estão associadas às seguintes chaves do dicionário $dReponse:

  • optoui: estado do botão de opção chamado optoui
  • optnon: estado do botão de opção chamado optnon
  • filhos: número de filhos a ser inserido no campo txtenfants
  • salário: salário anual a ser inserido no campo txtsalário

O script que gera a página de erros chama-se v-erreurs2.php. 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 visão, a serem definidas pelo gerador de visões, 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

Ela não sofreu alterações. Continua sendo style1.css.

4.16.5. O novo arquivo de configuração

Para implementar essas novas visualizações, precisamos alterar algumas linhas do arquivo 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 serem incluídos
  $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 visualizações 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 do aplicativo
  $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 do aplicativo
  $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 principal consiste em trocar os geradores de visualização associados aos relatórios [e-formulaire] e [e-erreurs]. Feito isso, os novos geradores de visualização ficam encarregados de gerar as novas páginas de resposta.

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

No arquivo de configuração, o relatório [e-formulaire] agora está associado ao gerador de visualização e-formulaire2.php. O código desse 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 ser gerado
    $type=$dSession['etat']['secondaire'];
  if($type=='init'){
      // formulário vazio
    $dReponse['optnon']='checked';
  }//if
  if($type=='calculimpot'){
      // é necessário exibir novamente os parâmetros de entrada 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'){
      // precisamos exibir novamente 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
  // enviamos a resposta
  finSession($dConfig,$dReponse,$dSession);
?>  

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

  • o gerador de visualização indica que deseja utilizar o modelo de resposta modele2
  • por esse motivo, ele preenche os elementos dinâmicos $dReponse['vue1'], $dReponse['vue2'], $dReponse['vue3'] — todos os três necessários para o modelo de resposta modele2.
  • O gerador também preenche o elemento dinâmico $dReponse['liens'], que define os links a serem exibidos na área [vue2] da resposta.

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

No arquivo de configuração, o relatório [e-erreurs] agora está associado ao gerador de visualização e-erreurs2.php. O código desse 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 complementares
  $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 feitas são idênticas às realizadas no gerador de visualização e-formulaire2.php.

4.17. Conclusão

Conseguimos demonstrar, por meio de um exemplo, a vantagem do nosso controlador genérico. Não precisamos escrevê-lo. Limitamo-nos a escrever os scripts das ações, dos geradores de visualização e das visualizações do aplicativo. Além disso, demonstramos a vantagem de separar as ações das visualizações. Assim, pudemos alterar a aparência das respostas sem modificar uma única linha de código dos scripts de ação. Apenas os scripts envolvidos na geração das visualizações foram modificados. Para que isso seja possível, o script de ação não deve fazer nenhuma suposição sobre a visualização que exibirá as informações que ele calculou. Ele deve se limitar a entregar essas informações ao controlador, que as transmite ao gerador de visualização, o qual as formatará. Essa é uma regra absoluta: uma ação deve estar completamente separada das visualizações.

Neste capítulo, abordamos a filosofia Struts, bem conhecida pelos desenvolvedores Java. Um projeto de código aberto chamado php.mvc permite o desenvolvimento web/PHP com a filosofia Struts. Consulte o site http://www.phpmvc.net/ para obter mais informações.