Skip to content

4. Een illustratief voorbeeld

We willen de voorgaande methode illustreren aan de hand van een voorbeeld van een belastingberekening.

4.1. Het probleem

We willen een programma schrijven waarmee de belasting van een belastingplichtige kan worden berekend. We gaan uit van het vereenvoudigde geval van een belastingplichtige die alleen zijn salaris hoeft aan te geven:

  • we berekenen het aantal aandelen van de werknemer nbParts=nbEnfants/2 +1 als hij ongehuwd is, nbEnfants/2+2 als hij getrouwd is, waarbij nbEnfants het aantal kinderen is. Het aantal delen wordt met 0,5 verhoogd als er drie of meer kinderen zijn.
  • zijn belastbaar inkomen R wordt berekend als R = 0,72 * S, waarbij S zijn jaarsalaris is
  • vervolgens wordt de gezinscoëfficiënt Q berekend als Q = R/N
  • zijn belasting I wordt berekend op basis van de volgende gegevens
grens
coeffR
coeffN
12620,0
0
0
13190
0,05
631
15640
0,1
1290,5
24.740
0,15
2072,5
31810
0,2
3309,5
39.970
0,25
4900
48360
0,3
6898,5
55790
0,35
9316,5
92970
0,4
12106
127860
0,45
16754,5
151250
0,50
23147,5
172040
0,55
30710
195000
0,60
39312
0
0,65
49062
Elke regel heeft 3 limietvelden: coeffR, coeffN. Om belasting I te berekenen, zoeken we de eerste regel waarbij QF ≤ de limiet is. Als QF bijvoorbeeld 30000 is, vinden we de regel: 31810 0,2 3309,5. De belasting I is dan gelijk aan 0,2*R - 3309,5*nbParts. Als QF zodanig is dat de relatie QF <= limiet nooit geldt, dan worden de coëfficiënten van de laatste regel gebruikt: 0 0,65 49062, wat de belasting I = 0,65*R oplevert - 49062*nbParts.

4.2. De database

De bovenstaande gegevens worden opgeslagen in een database met de naam MySQL, genaamd dbimpots. De gebruiker seldbimpots met wachtwoord mdpseldbimpots heeft alleen-lezen toegang tot de inhoud van de database. Deze database bevat één enkele tabel met de naam impots, waarvan de structuur als volgt is:

Image

De inhoud ervan is als volgt:

Image

4.3. De architectuur MVC van de applicatie

De applicatie zal de volgende architectuur MVC hebben:

  • de controller main.php is de eerder beschreven generieke controller
  • het verzoek van de client wordt naar de controller verzonden in de vorm van een verzoek met de indeling main.php?action=xx. De waarde van de parameter `action` bepaalt welk script van het blok `ACTIONS` moet worden uitgevoerd. Het uitgevoerde actiescript levert aan de controller een variabele die aangeeft in welke status de webapplicatie moet worden geplaatst. Op basis van deze status activeert de controller een van de weergavegeneratoren om het antwoord naar de client te sturen.
  • impots-data.php is de klasse die de controller voorziet van de gegevens die deze nodig heeft
  • impots-calcul.php is de bedrijfslogische klasse waarmee de belasting wordt berekend

4.4. De klasse voor gegevenstoegang

De klasse voor gegevenstoegang is ontworpen om de herkomst van de gegevens voor de webapplicatie te verbergen. In de interface ervan vinden we een methode getData die de drie gegevensarrays levert die nodig zijn voor de berekening van de belasting. In ons voorbeeld worden de gegevens uit een database MySQL gehaald. Om de klasse onafhankelijk te maken van het werkelijke type van SGBD, gebruiken we de bibliotheek pear::DB die in de bijlage wordt beschreven. De code van de klasse is als volgt:

<?php

  // bibliotheken
  require_once 'DB.php';

  class impots_data{  
    // toegangsklasse tot de gegevensbron DBIMPOTS

      // attributen
    var $sDSN;                    // de verbindingsreeks
      var $sDatabase;            // de naam van de database
    var $oDB;                        // verbinding met de database
    var $aErreurs;            // foutenlijst
    var $oRésultats;        // resultaat van een query
        var $connecté;            // booleaanse waarde die aangeeft of er al dan niet verbinding is met de database
        var $sQuery;                // de laatst uitgevoerde query

    // constructor
    function impots_data($dDSN){

        // $dDSN: woordenboek waarin de te tot stand te brengen verbinding wordt gedefinieerd
      // $dDSN['sgbd']: het type van de SGBD waarmee verbinding moet worden gemaakt
      // $dDSN['host']: de naam van de hostmachine waarop het zich bevindt      
      // $dDSN['database']: de naam van de database waarmee verbinding moet worden gemaakt      
      // $dDSN['user']: een gebruiker van de database
      // $dDSN['mdp']: zijn wachtwoord

      // maakt in $oDB een verbinding met de database die is gedefinieerd door $dDSN onder de identiteit van $dDSN['user']
      // als de verbinding tot stand komt  
          // plaatst in $sDSN de verbindingsstring voor de database
          // stelt in $sDataBase de naam in van de database waarmee verbinding wordt gemaakt
        // stelt $connecté in op waar
      // als de verbinding mislukt
          // voegt de juiste foutmeldingen toe aan de lijst $aErreurs
        // sluit de verbinding indien nodig
        // zet $connecté op ‘false’ 

      // wist de lijst met fouten
            $this->aErreurs=array();

      // er wordt een verbinding met de database $sDSN tot stand gebracht
      $this->sDSN=$dDSN["sgbd"]."://".$dDSN["user"].":".$dDSN["mdp"]."@".$dDSN["host"]."/".$dDSN["database"];
      $this->sDatabase=$dDSN["database"];
      $this->connect();

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

      // verbinding tot stand gebracht     
      $this->connecté=TRUE;     
    }//fabrikant

    // ------------------------------------------------------------------
    function connect(){
        // (opnieuw) verbinding maken met de database
      // foutenlijst wissen
            $this->aErreurs=array();

      // er wordt een verbinding met de database tot stand gebracht $sDSN
        $this->oDB=DB::connect($this->sDSN,true);

        // fout?
        if(DB::iserror($this->oDB)){
          // de fout wordt genoteerd
          $this->aErreurs[]="Echec de la connexion à la base [".$this->sDatabase."] : [".$this->oDB->getMessage()."]";
        // de verbinding is mislukt
        $this->connecté=FALSE;
        // einde
        return;
      }

      // verbinding tot stand gebracht
      $this->connecté=TRUE;
    }//verbinden

    // ------------------------------------------------------------------
    function disconnect(){
      // als er verbinding is, wordt de verbinding met de database verbroken $sDSN
        if($this->connecté){
              $this->oDB->disconnect();
          // de verbinding is verbroken
        $this->connecté=FALSE;
            }//if
    }//verbinding verbreken

    // -------------------------------------------------------------------
    function execute($sQuery){
        // $sQuery: verzoek moet worden uitgevoerd

        // de aanvraag wordt opgeslagen
            $this->sQuery=$sQuery;    

      // is er verbinding?
      if(! $this->connecté){
          // de fout wordt genoteerd
        $this->aErreurs[]="Pas de connexion existante à la base [$this->sDatabase]";
        // einde
        return;
      }//if

      // het verzoek wordt uitgevoerd
      $this->oRésultats=$this->oDB->query($sQuery);

        // fout?
        if(DB::iserror($this->oRésultats)){
            // de fout wordt genoteerd
            $this->aErreurs[]="Echec de la requête [$sQuery] : [".$this->oRésultats->getMessage()."]";
          // terug
        return;
      }//if     
    }//uitvoeren

    // ------------------------------------------------------------------
    function getData(){
      // de 3 reeksen grenswaarden, coeffr en coeffn worden opgehaald
      $this->execute('select limites, coeffR, coeffN from impots');
      // fouten?
      if(count($this->aErreurs)!=0) return array();
      // we doorlopen het resultaat van de select
      while ($ligne = $this->oRésultats->fetchRow(DB_FETCHMODE_ASSOC)) {
        $limites[]=$ligne['limites'];
        $coeffr[]=$ligne['coeffR'];
        $coeffn[]=$ligne['coeffN'];                
      }//while
      return array($limites,$coeffr,$coeffn);      
    }//getDataImpots

  }//klasse
?>      

Een testprogramma zou er als volgt uit kunnen zien:

<?php

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

    // test van de klasse impots-data
  ini_set('track_errors','on');
  ini_set('display_errors','on');

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

  // sessie openen
  $oImpots=new impots_data($dDSN);
  // fouten?
  if(checkErreurs($oImpots)){
    exit(0);
  }
  // opvolging
  echo "Connecté à la base...\n";

  // ophalen van grenswaarden, coeffr, coeffn
  list($limites,$coeffr,$coeffn)=$oImpots->getData();
  // fouten?
  if( ! checkErreurs($oImpots)){
    // inhoud
    echo "données : \n";
    for($i=0;$i<count($limites);$i++){
      echo "[$limites[$i],$coeffr[$i],$coeffn[$i]]\n";
    }//for
  }//if

  // verbinding wordt verbroken
  $oImpots->disconnect();
  // vervolg
  echo "Déconnecté de la base...\n";  
  // einde
  exit(0);

  // ----------------------------------
  function checkErreurs(&$oImpots){
      // fouten?
    if(count($oImpots->aErreurs)!=0){
        // weergave
      for($i=0;$i<count($oImpots->aErreurs);$i++){
          echo $oImpots->aErreurs[$i]."\n";
      }//voor
      // fouten
      return true;
    }//if
    // geen fouten
    return false;
  }//checkErreurs  

?>     

Het uitvoeren van dit testprogramma levert de volgende resultaten op:

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. De klasse voor belastingberekening

Met deze klasse kan de belasting van een belastingplichtige worden berekend. De gegevens die nodig zijn voor deze berekening worden aan de constructor ervan doorgegeven. Deze zorgt vervolgens voor de berekening van de bijbehorende belasting. De code van de klasse is als volgt:

<?php

  class impots_calcul{  
    // belastingberekeningsklasse

    // constructor
    function impots_calcul(&$perso,&$data){
      // $perso: woordenboek met de volgende sleutels
      // kinderen: aantal kinderen
      // salaris: jaarsalaris
      // getrouwd: booleaanse waarde die aangeeft of de belastingplichtige al dan niet getrouwd is
      // belasting(en): te betalen belasting berekend door deze constructor
      // $data: woordenboek met de volgende sleutels
      // grenzen: tabel met de grenzen van de inkomensschijven
      // coëfficiënten: tabel met de inkomenscoëfficiënten
      // coeffn: tabel met coëfficiënten voor het aantal aandelen
      // de 3 tabellen hebben hetzelfde aantal elementen

      // berekening van het aantal aandelen
      if($perso['marié'])
        $nbParts=$perso['enfants']/2+2;
        else $nbParts=$perso['enfants']/2+1;
      if ($perso['enfants']>=3) $nbParts+=0.5;

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

      // gezinsquotiënt
      $QF=$revenu/$nbParts;

      // opzoeken van de belastingschijf die overeenkomt met QF
      $nbTranches=count($data['limites']);
      $i=0;
      while($i<$nbTranches-2 && $QF>$data['limites'][$i]) $i++;

      // de belasting
      $perso['impot']=floor($data['coeffr'][$i]*$revenu-$data['coeffn'][$i]*$nbParts);
    }//bouwer
  }//klasse
?>

Een testprogramma zou er als volgt uit kunnen zien:

<?php

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

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

  // aanmelding
  $oImpots=new impots_data($dDSN);
  // fouten?
  if(checkErreurs($oImpots)){
    exit(0);
  }
  // opvolging
  echo "Connecté à la base...\n";  
  // ophalen van grenswaarden, coeffr, coeffn
  list($limites,$coeffr,$coeffn)=$oImpots->getData();
  // fouten?
  if(checkErreurs($oImpots)){
    exit(0);
  }
  // verbinding wordt verbroken
  $oImpots->disconnect();
  // opvolging
  echo "Déconnecté de la base...\n";  

  // berekening van een belasting
  $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);

  // einde
  exit(0);

  // ----------------------------------
  function checkErreurs(&$oImpots){
      // fouten?
    if(count($oImpots->aErreurs)!=0){
        // weergave
      for($i=0;$i<count($oImpots->aErreurs);$i++){
          echo $oImpots->aErreurs[$i]."\n";
      }//voor
      // fouten
      return true;
    }//if
    // geen fouten
    return false;
  }//checkErreurs  

?>        

Het uitvoeren van dit testprogramma levert de volgende resultaten op:

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. De werking van de applicatie

Wanneer de webapplicatie voor belastingberekening wordt gestart, verschijnt het volgende scherm [v-formulaire]:

De gebruiker vult de velden in en vraagt de belastingberekening aan:

Opvallend is dat het formulier opnieuw wordt gegenereerd in de staat waarin de gebruiker het heeft gevalideerd en dat bovendien het te betalen belastingbedrag wordt weergegeven. De gebruiker kan invoerfouten maken. Deze worden aan hem gemeld via een foutpagina die we de weergave [v-erreurs] zullen noemen.

Via de link [Retour au formulaire de saisie] kan de gebruiker het formulier terugvinden zoals hij het heeft gevalideerd.

Ten slotte zet de knop [Effacer le formulaire] het formulier terug in de oorspronkelijke staat, c.a.d, zoals de gebruiker het bij de eerste aanvraag heeft ontvangen.

4.7. Terug naar de architectuur MVC van de applicatie

De applicatie heeft de volgende architectuur: MVC:

We hebben zojuist de twee klassen impots-data.php en impots-calcul.php beschreven. Nu beschrijven we de overige onderdelen van de architectuur.

4.8. De controller van de applicatie

De applicatiecontroller main.php is dezelfde als die in het eerste deel van dit hoofdstuk is beschreven. Het is een generieke, applicatie-onafhankelijke controller.

<?php
     // generieke controller

  // configuratie lezen
  include 'config.php';

  // bibliotheken opnemen
  for($i=0;$i<count($dConfig['includes']);$i++){
      include($dConfig['includes'][$i]);
  }//for  

  // de sessie starten of hervatten
  session_start();
  $dSession=$_SESSION["session"];
  if($dSession) $dSession=unserialize($dSession);

  // de uit te voeren actie ophalen
  $sAction=$_GET['action'] ? strtolower($_GET['action']) : 'init';
  $sAction=strtolower($_SERVER['REQUEST_METHOD']).":$sAction";

     // is de volgorde van de acties normaal?
  if( ! enchainementOK($dConfig,$dSession,$sAction)){  
    // afwijkende volgorde
    $sAction='enchainementinvalide';
  }//if

     // verwerking van de actie
  $scriptAction=$dConfig['actions'][$sAction] ? 
    $dConfig['actions'][$sAction]['url'] : 
    $dConfig['actions']['actionInvalide']['url'];
  include $scriptAction;


  // het antwoord (weergave) naar de klant verzenden
  $sEtat=$dSession['etat']['principal'];
  $scriptVue=$dConfig['etats'][$sEtat]['vue'];
  include $scriptVue;

  // einde van het script – dit zou niet mogen gebeuren, tenzij er een bug is
  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: configuratiedictionary
      // $dSession: woordenboek met sessie-informatie
         // $dReponse: het woordenboek met de argumenten van de responspagina

    // sessie-record
    if(isset($dSession)){
      //: de parameters van het verzoek worden in de sessie opgeslagen
      $dSession['requete']=strtolower($_SERVER['REQUEST_METHOD'])=='get' ? $_GET :
          strtolower($_SERVER['REQUEST_METHOD'])=='post' ? $_POST : array();
        $_SESSION['session']=serialize($dSession);
      session_write_close();
    }else{    
        // geen sessie
      session_destroy();
    }

        // het antwoord wordt weergegeven
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    // einde van het script
    exit(0);
  }//sessie beëindigen      

  //--------------------------------------------------------------------
    function enchainementOK(&$dConfig,&$dSession,$sAction){
      // controleert of de huidige actie is toegestaan gezien de vorige status
    $etat=$dSession['etat']['principal'];
    if(! isset($etat)) $etat='sansetat';

    // actiecontrole
    $actionsautorisees=$dConfig['etats'][$etat]['actionsautorisees'];
    $autorise= ! isset($actionsautorisees) || in_array($sAction,$actionsautorisees);
        return $autorise;    
  }

  //--------------------------------------------------------------------
  function dump($dInfos){
      // geeft een informatieoverzicht weer
    while(list($clé,$valeur)=each($dInfos)){
        echo "[$clé,$valeur]<br>\n";
    }//while
  }//opvolging

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

4.9. De acties van de webapplicatie

Er zijn vier acties:

  • get:init: dit is de actie die wordt geactiveerd bij het eerste verzoek zonder parameters aan de controller. Deze actie genereert de lege 'formulier'-weergave.
  • post:effacerformulaire: actie die wordt geactiveerd door de knop [Effacer le formulaire]. Deze genereert de lege weergave [v-formulaire].
  • post:belastingberekenen: actie die wordt geactiveerd door de knop [Calculer l'impôt]. Deze genereert ofwel de weergave [v-formulaire] met het te betalen belastingbedrag, ofwel de weergave [v-erreurs].
  • get:retourformulaire: actie geactiveerd door de link [Retour au formulaire de saisie]. Deze genereert de weergave [v-formulaire], vooraf ingevuld met de foutieve gegevens.

Deze acties zijn als volgt geconfigureerd in het configuratiebestand:

<?php

  // configuratie van de acties van de applicatie
  $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');          

Aan elke actie is het script gekoppeld dat verantwoordelijk is voor de verwerking ervan. Elke actie brengt de webapplicatie in een toestand die wordt bepaald door het element $dSession['etat']['principal']. Deze toestand is bedoeld om in de sessie te worden opgeslagen. Bovendien slaat de actie in het woordenboek $dReponse de informatie op die nodig is voor de weergave van de weergave die verband houdt met de nieuwe toestand waarin de applicatie zich zal bevinden.

4.10. De rapporten van de webapplicatie

Er zijn er twee:

  • [e-formulaire]: toestand waarin de verschillende varianten van de weergave [v-formulaire] worden getoond.
  • [e-erreurs]: toestand waarin de weergave [v-erreurs] wordt getoond.

De toegestane acties in deze statussen zijn de volgende:

<?php

  // configuratie van de rapporten van de applicatie
  $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'));        

In een status komen de toegestane acties overeen met de doelen van de links of knoppen in de weergave die aan de status is gekoppeld. Bovendien is de actie 'get:init' altijd toegestaan. Hierdoor kan de gebruiker de URL main.php uit de lijst met URL in zijn browser ophalen en opnieuw afspelen, ongeacht de status van de applicatie. Dit is een soort 'handmatige' reset. De status ‘sansetat’ bestaat alleen bij het opstarten van de applicatie.

Aan elke status van de applicatie is een script gekoppeld dat de bijbehorende weergave genereert:

  • status [e-formulaire]: script e-formulaire.php
  • rapport [e-erreurs]: script e-erreurs.php

Het rapport [e-formulaire] toont de weergave [v-formulaire] met varianten. De weergave [v-formulaire] kan namelijk leeg, vooraf ingevuld of met het belastingbedrag worden weergegeven. De actie die de applicatie naar het rapport [e-formulaire] brengt, specificeert in de variabele $dSession['etat']['principal'] het hoofdrapport van de applicatie. De controller gebruikt alleen deze informatie. In onze applicatie zal de actie die naar de status [e-formulaire] leidt, in $dSession['etat']['secondaire'] aanvullende informatie toevoegen, zodat de responsgenerator weet ofer een leeg of vooraf ingevuld formulier moet worden gegenereerd, met of zonder het belastingbedrag. We hadden het ook anders kunnen aanpakken door ervan uit te gaan dat er hier sprake was van drie verschillende statussen en dus drie weergavegeneratoren moesten worden geschreven.

4.11. Het configuratiebestand config.php van de webapplicatie

<?php

     // configuratie van PHP
  ini_set("register_globals","off");
  ini_set("display_errors","off");  
  ini_set("expose_php","off");

  // lijst met op te nemen modules
  $dConfig['includes']=array('c-impots-data.php','c-impots-calcul.php');

  // controller van de applicatie
  $dConfig['webapp']=array('titre'=>"Calculez votre impôt");

  // configuratie van de weergaven van de applicatie
  $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';  

  // configuratie van de acties van de applicatie
  $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');          

  // configuratie van de rapporten van de applicatie
  $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'));

  // configuratie van het applicatiemodel
    $dConfig["DSN"]=array(
        "sgbd"=>"mysql",
        "user"=>"seldbimpots",
        "mdp"=>"mdpseldbimpots",
        "host"=>"localhost",
        "database"=>"dbimpots"
    );
?>

4.12. De acties van de webapplicatie

4.12.1. Algemene werking van actiescripts

  • Een actiescript wordt door de controller aangeroepen op basis van de actieparameter die deze van de client heeft ontvangen.
  • Na uitvoering moet het actiescript aan de controller aangeven in welke status de applicatie moet worden geplaatst. Deze status moet worden opgegeven in $dSession['etat']['principal'].
  • Een actiescript kan informatie in de sessie willen plaatsen. Dit doet het door deze informatie in het woordenboek $dSession op te nemen; dit woordenboek wordt aan het einde van de verzoek-antwoordcyclus automatisch door de controller in de sessie opgeslagen.
  • Een actiescript kan informatie hebben die aan de weergaven moet worden doorgegeven. Dit aspect staat los van de controller. Het betreft de interface tussen de acties en de weergaven, een interface die specifiek is voor elke toepassing. In het hier besproken voorbeeld zullen de acties informatie aan de weergavegeneratoren verstrekken via een woordenboek met de naam $dReponse.

4.12.2. De actie get:init

Dit is de actie die het lege formulier genereert. Uit het configuratiebestand blijkt dat deze wordt verwerkt door het script a-init.php:

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

De code van het script a-init.php is als volgt:

<?php
     // het invoerformulier wordt weergegeven
  $dSession['etat']=array('principal'=>'e-formulaire', 'secondaire'=>'init');
?>  

Dit script zorgt er alleen voor dat in $dSession['etat']['principal'] de status wordt vastgelegd waarin de applicatie zich moet bevinden, namelijk de status [e-formulaire], en voegt in $dSession['etat']['secondaire'] een specificatie over deze status toe. Uit het configuratiebestand blijkt dat de controller het script e-formulaire.php zal uitvoeren om het antwoord aan de klant te genereren.

<?php

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

Het script e-formulaire.php genereert de weergave [v-formulaire] in de varianten [init] en c.a.d: het lege formulier.

4.12.3. De actie post:calculerimpot

Dit is de actie waarmee de belasting wordt berekend op basis van de gegevens die in het formulier zijn ingevoerd. Het configuratiebestand geeft aan dat het script a-calculimpot.php deze actie zal verwerken:

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

De code van het script a-calculimpot.php is als volgt:

<?php
     // verzoek om belastingberekening

  // eerst wordt de geldigheid van de parameters gecontroleerd
  $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é";
  }

  // als er fouten zijn, is het proces beëindigd
  if(count($erreurs)!=0){
      // de foutpagina wordt voorbereid
    $dReponse['erreurs']=&$erreurs;
    $dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'saisie');
      return;
  }//if

  // de ingevoerde gegevens correct zijn
  // de gegevens die nodig zijn voor de belastingberekening worden opgehaald
  if(! $dSession['limites']){
      // de gegevens bevinden zich niet in de sessie
    // de gegevens worden opgehaald uit de gegevensbron
    list($erreurs,$limites,$coeffr,$coeffn)=getData($dConfig['DSN']);
    // als er fouten zijn, wordt de foutpagina weergegeven
    if(count($erreurs)!=0){
        // de foutpagina wordt voorbereid
      $dReponse['erreurs']=&$erreurs;
        $dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'database');
      return;
    }//if
    // geen fouten – de gegevens worden in de sessie opgeslagen
    $dSession['limites']=&$limites;
    $dSession['coeffr']=&$coeffr;
    $dSession['coeffn']=&$coeffn;
  }//if

  // hier beschikken we over de gegevens die nodig zijn voor de berekening van de belasting
  // we berekenen deze
  $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);

    // voorbereiding van de antwoordpagina
  $dSession['etat']=array('principal'=>'e-formulaire','secondaire'=>'calculimpot');
  $dReponse['impot']=$dPerso['impot'];
  return;

  //-----------------------------------------------------------------------
  function getData($dDSN){
      // verbinding maken met de gegevensbron die is gedefinieerd in het woordenboek $dDSN
        $oImpots=new impots_data($dDSN);
    if(count($oImpots->aErreurs)!=0) return array($oImpots->aErreurs);
    // ophalen van de grenswaarden, coeffr, coeffn
        list($limites,$coeffr,$coeffn)=$oImpots->getData();
         // verbinding wordt verbroken
        $oImpots->disconnect();
    // het resultaat wordt teruggestuurd
    if(count($oImpots->aErreurs)!=0) return array($oImpots->aErreurs);
        else return array(array(),$limites,$coeffr,$coeffn);
  }//getData

Het script doet wat het moet doen: de belasting berekenen. We laten het aan de lezer over om de code van de verwerking te ontcijferen. Wij richten ons op de statussen die als gevolg van deze actie kunnen optreden:

  • de ingevoerde gegevens zijn onjuist of de toegang tot de gegevens verloopt niet goed: de applicatie wordt in de status [e-erreurs] geplaatst. Uit het configuratiebestand blijkt dat het script e-erreurs.php verantwoordelijk is voor het genereren van de responsweergave:
<?php

  $dConfig['etats']['e-erreurs']=array(
      'actionsautorisees'=>array('get:retourformulaire','get:init'),
      'vue'=>'e-erreurs.php');
  • in alle andere gevallen wordt de toepassing in de status [e-formulaire] geplaatst met de berekeningsvariant die is opgegeven in $dSession['etat']['secondaire']. Uit het configuratiebestand blijkt dat het script e-formulaire.php de responsweergave zal genereren. Het gebruikt de waarde van $dSession['etat']['secondaire'] om een vooraf ingevuld formulier te genereren met de door de gebruiker ingevoerde waarden en bovendien het belastingbedrag.

4.12.4. De actie post:effacerformulaire

Deze is via de configuratie gekoppeld aan het reeds beschreven script a-init.php.

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

4.12.5. De get-actie:formulierterug

Hiermee kun je vanuit de status [e-erreurs] terugkeren naar de status [e-formulaire]. Het script a-retourformulaire.php verwerkt deze actie:

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

Het script a-retourformulaire.php ziet er als volgt uit:

<?php
     // het invoerformulier wordt weergegeven
  $dSession['etat']=array('principal'=>'e-formulaire','secondaire'=>'retourformulaire');
?>    

Er wordt simpelweg gevraagd om de applicatie in de status [e-formulaire] te plaatsen, in de variant [retourformulaire]. Uit het configuratiebestand blijkt dat de controller het script e-formulaire.php zal uitvoeren om het antwoord aan de klant te genereren.

<?php

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

Het script e-formulaire.php genereert de weergave [v-formulaire] in de varianten [retourformulaire] en c.a.d. het formulier dat vooraf is ingevuld met de door de gebruiker ingevoerde waarden, maar zonder het belastingbedrag.

4.13. De ongeldige reeks acties

De geldige acties vanuit een bepaalde toestand van de applicatie worden bepaald door de configuratie:

<?php

  // configuratie van de applicatiestatus
  $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'));

We hebben deze configuratie al uitgelegd. Als er een ongeldige reeks acties wordt gedetecteerd, wordt het script a-enchainementinvalide.php uitgevoerd:

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

De code van dit script is als volgt:

<?php 
     // ongeldige reeks acties
  $dReponse['erreurs']=array("Enchaînement d'actions invalide");
  $dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'enchainementinvalide');  
?>

Dit houdt in dat de applicatie in de status [e-erreurs] wordt geplaatst. In $dSession['etat']['secondaire'] geven we informatie op die door de generator van de foutpagina wordt gebruikt. Zoals we al hebben gezien, is deze generator e-erreurs.php:

<?php

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

We zullen de code van deze generator later bekijken. De weergave die naar de klant wordt verzonden, ziet er als volgt uit:

Image

4.14. De weergaven van de applicatie

4.14.1. Weergave van de uiteindelijke weergave

Laten we eens kijken hoe de controller het antwoord naar de client stuurt, nadat de door de client gevraagde actie is uitgevoerd:

<?php

....
  // de sessie wordt gestart of hervat
  session_start();
  $dSession=$_SESSION["session"];
  if($dSession) $dSession=unserialize($dSession);

  // de uit te voeren actie wordt opgehaald
  $sAction=$_GET['action'] ? strtolower($_GET['action']) : 'init';
  $sAction=strtolower($_SERVER['REQUEST_METHOD']).":$sAction";

     // is de reeks acties normaal?
  if( ! enchainementOK($dConfig,$dSession,$sAction)){  
    // afwijkende reeks
    $sAction='enchainementinvalide';
  }//if

     // de actie wordt verwerkt
  $scriptAction=$dConfig['actions'][$sAction] ? 
    $dConfig['actions'][$sAction]['url'] : 
    $dConfig['actions']['actionInvalide']['url'];
  include $scriptAction;

  // het antwoord (weergave) naar de klant verzenden
  $sEtat=$dSession['etat']['principal'];
  $scriptVue=$dConfig['etats'][$sEtat]['vue'];
  include $scriptVue;

.....

  // ---------------------------------------------------------------
  function finSession(&$dConfig,&$dReponse,&$dSession){
    // $dConfig: configuratiedictionary
      // $dSession: woordenboek met sessie-informatie
         // $dReponse: het woordenboek met de argumenten van de antwoordpagina

    // sessie vastleggen
...

         // verzending van het antwoord naar de klant
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    // einde van het script
    exit(0);
  }//einde sessie      

Bij terugkeer van een actiescript haalt de controller uit $dSession['etat']['principal'] de status op waarin de applicatie moet worden gezet. Deze status is bepaald door de actie die zojuist is uitgevoerd. De controller laat vervolgens de aan de status gekoppelde weergavegenerator uitvoeren. De naam hiervan vindt hij in het configuratiebestand. De rol van de weergavegenerator is als volgt:

  • bepaalt in $dReponse['vuereponse'] de naam van het te gebruiken responsmodel. Deze informatie wordt doorgegeven aan de controller. Een model is een samenstelling van elementaire weergaven die samen de uiteindelijke weergave vormen.
  • bereidt de dynamische informatie voor die in de uiteindelijke weergave moet worden getoond. Dit punt staat los van de controller. Het betreft de interface tussen de weergavegenerator en de uiteindelijke weergave. Deze is specifiek voor elke toepassing.
  • moet verplicht worden afgesloten met een aanroep van de functie finSession van de controller. Deze functie zal
    • de sessie opslaan
    • het antwoord verzenden

De code van de functie finSession is als volgt:

<?php

  // ---------------------------------------------------------------
  function finSession(&$dConfig,&$dReponse,&$dSession){
    // $dConfig: configuratielijst
      // $dSession: woordenboek met sessie-informatie
         // $dReponse: het woordenboek met de argumenten van de responspagina

    // sessie-registratie
...

         // verzending van het antwoord naar de klant
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    // einde van het script
    exit(0);
  }//einde sessie      

De weergave die naar de gebruiker wordt verzonden, wordt bepaald door de entiteit $dReponse['vuereponse'], die het sjabloon definieert dat voor het uiteindelijke antwoord moet worden gebruikt.

4.14.2. Sjabloon voor het antwoord

De applicatie genereert haar verschillende antwoorden volgens het volgende unieke sjabloon:

Dit model is gekoppeld aan sleutel model1 uit het woordenboek $dConfig['vuesreponse']:

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

Het script m-reponse.php is verantwoordelijk voor het genereren van dit sjabloon:

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

Dit script bevat drie dynamische elementen die zijn opgeslagen in een woordenboek met de naam $dReponse en gekoppeld zijn aan de volgende sleutels:

  • urlstyle: URL van het stylesheet van het sjabloon
  • vue1: naam van het script dat verantwoordelijk is voor het genereren van de weergave vue1
  • vue2: naam van het script dat verantwoordelijk is voor het genereren van de weergave vue2

Een weergavegenerator die het sjabloon modèle1 wil gebruiken, moet deze drie dynamische elementen definiëren. We definiëren nu de elementaire weergaven die de plaats kunnen innemen van de elementen [vue1] en [vue2] van het sjabloon.

4.14.3. De basisweergave v-bandeau.php

Het script v-bandeau.php genereert een weergave die in het gebied [vue1] wordt geplaatst:

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

De weergavegenerator moet twee dynamische elementen definiëren die in een woordenboek $dReponse zijn geplaatst en aan de volgende sleutels zijn gekoppeld:

  • titel: weer te geven titel
  • resultaat: te betalen belastingbedrag

4.14.4. De elementaire weergave v-formulaire.php

Het gedeelte [vue2] komt overeen met ofwel de weergave [v-formulaire] ofwel de weergave [v-erreurs]. De weergave [v-formulaire] wordt gegenereerd door het 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>                                            

De dynamische onderdelen van deze weergave die door de weergavegenerator moeten worden gedefinieerd, zijn gekoppeld aan de volgende sleutels in het woordenboek $dReponse:

  • optoui: status van de keuzeknop met de naam optoui
  • optnon: status van de keuzeknop met de naam optnon
  • kinderen: aantal kinderen dat in het veld txtenfants moet worden geplaatst
  • salaire: jaarsalaris dat in het veld txtsalaire moet worden geplaatst

4.14.5. De elementaire weergave v-erreurs.php

De weergave [v-erreurs] wordt gegenereerd door het 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>

De dynamische onderdelen van deze weergave, die door de weergavegenerator moeten worden gedefinieerd, zijn gekoppeld aan de volgende sleutels in het woordenboek $dReponse:

  • fouten: tabel met foutmeldingen
  • info: informatiemelding
  • link: tekst van een link
  • href: doel-URL van de bovenstaande link

4.14.6. Het stylesheet

Alle weergaven worden 'aangekleed' door een stylesheet. Om het uiterlijk van de applicatie te wijzigen, past u het stylesheet aan. Het volgende stylesheet 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. Weergavegeneratoren

4.15.1. De rol van een weergavegenerator

Laten we nog eens kijken naar de codereeks in de controller die een viewgenerator activeert:

<?php

     // verwerking van de actie
  $scriptAction=$dConfig['actions'][$sAction] ? 
    $dConfig['actions'][$sAction]['url'] : 
    $dConfig['actions']['actionInvalide']['url'];
  include $scriptAction;

  // verzending van het antwoord (weergave) naar de klant
  $sEtat=$dSession['etat']['principal'];
  $scriptVue=$dConfig['etats'][$sEtat]['vue'];
  include $scriptVue;

Een weergavegenerator is gekoppeld aan de toestand waarin de applicatie wordt geplaatst. De koppeling tussen toestand en weergavegenerator wordt via de configuratie vastgelegd:

<?php

  // configuratie van de applicatiestatus
  $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'));

We hebben al aangegeven wat de rol van een weergavegenerator is. Laten we dat hier nog eens herhalen. Een weergavegenerator:

  • legt in $dReponse['vuereponse'] de naam vast van het te gebruiken responsmodel. Deze informatie wordt doorgegeven aan de controller. Een model is een samenstelling van elementaire weergaven die samen de uiteindelijke weergave vormen.
  • bereidt de dynamische informatie voor die in de uiteindelijke weergave moet worden getoond. Dit gebeurt onafhankelijk van de controller. Het betreft de interface tussen de weergavegenerator en de uiteindelijke weergave. Deze is specifiek voor elke toepassing.
  • moet verplicht worden afgesloten met een aanroep van de functie finSession van de controller. Deze functie zal
    • de sessie opslaan
    • het antwoord verzenden

4.15.2. De weergavegenerator die gekoppeld is aan de status [e-formulaire]

Het script dat verantwoordelijk is voor het genereren van de weergave die bij de status [e-formulaire] hoort, heet e-formulaire.php:

<?php

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

De code is als volgt:

<?php
  // het formulierantwoord wordt voorbereid
  $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'];

  // configuratie op basis van het te genereren formuliertype
    $type=$dSession['etat']['secondaire'];
  if($type=='init'){
      // leeg formulier
    $dReponse['optnon']='checked';
  }//if
  if($type=='calculimpot'){
      // moeten we de in de query opgeslagen invoerparameters opnieuw weergeven
    $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'){
      // moeten we de in de sessie opgeslagen invoerparameters opnieuw weergeven
    $dReponse['optoui']=$dSession['requete']['optmarie']=='oui' ? 'checked' : '';
    $dReponse['optnon']=$dReponse['optoui']=='' ? 'checked' : '';  
    $dReponse['enfants']=$dSession['requete']['txtenfants'];
    $dReponse['salaire']=$dSession['requete']['txtsalaire'];
  }//if
  // we sturen het antwoord
  finSession($dConfig,$dReponse,$dSession);
?>      

Merk op dat de weergavegenerator voldoet aan de voorwaarden die aan een weergavegenerator worden gesteld:

  • in $dReponse['vuereponse'] het te gebruiken antwoordmodel vast te leggen
  • informatie doorgeven aan dit sjabloon. Hier gebeurt dit via het woordenboek $dReponse.
  • afsluiten met de aanroep van de functie finSession van de controller

Hier wordt het model ‘model1’ gebruikt. Daarom definieert de generator de twee gegevens die dit model nodig heeft: $dReponse['vue1'] en $dReponse['vue2'].

In het specifieke geval van onze applicatie is de weergave die bij de status [e-formulaire] hoort, afhankelijk van informatie die is opgeslagen in de variabele $dSession['etat']['secondaire']. Dit is een ontwikkelingskeuze. Een andere applicatie zou kunnen besluiten om aanvullende informatie op een andere manier door te geven. Bovendien wordt hier alle informatie die nodig is voor de weergave van het uiteindelijke scherm in het woordenboek $dReponse geplaatst. Ook hier is het een keuze die aan de ontwikkelaar is. De status [e-formulaire] kan optreden na vier verschillende acties: init, belastingberekenen, formulier terugsturen, formulier wissen. De weer te geven weergave is niet in alle gevallen precies hetzelfde. Daarom zijn hier in $dSession['etat']['secondaire'] drie gevallen onderscheiden:

  • init: het formulier wordt leeg weergegeven
  • belastingberekening: het formulier wordt weergegeven met het belastingbedrag en de gegevens die tot de berekening hebben geleid
  • retourformulaire: het formulier wordt weergegeven met de oorspronkelijk ingevoerde gegevens

Hierboven gebruikt het script e-formulaire.php deze informatie om het antwoord in deze drie varianten weer te geven.

4.15.3. De weergave die bij het rapport [e-erreurs] hoort

Het script dat verantwoordelijk is voor het genereren van de weergave die bij rapport [e-erreurs] hoort, heet e-erreurs.php en ziet er als volgt uit:

<?php

  // we bereiden het foutantwoord voor
  $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';

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

  // het antwoord wordt verzonden
  finSession($dConfig,$dReponse,$dSession);
?>

4.15.4. Weergave van de uiteindelijke weergave

De twee scripts die de twee eindweergaven genereren, eindigen beide met een aanroep van de functie finSession van de controller:

<?php

  function finSession(&$dConfig,&$dReponse,&$dSession){
    // $dConfig: configuratiedictionary
      // $dSession: woordenboek met sessie-informatie
         // $dReponse: het woordenboek met de argumenten van de antwoordpagina

....

         // het antwoord wordt weergegeven
        include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];

    // einde van het script
    exit(0);
  }//einde sessie      

De weergave die naar de gebruiker wordt verzonden, wordt bepaald door de entiteit $dReponse['vuereponse'], die het sjabloon definieert dat voor het uiteindelijke antwoord moet worden gebruikt. Voor de rapporten [e-formulaire] en [e-erreurs] is dit sjabloon gedefinieerd als modele1:

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

Dit sjabloon komt qua configuratie overeen met het script m-reponse.php:

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

4.16. Het responsmodel wijzigen

We gaan er hier vanuit dat we besluiten het uiterlijk van het antwoord aan de klant te wijzigen en willen begrijpen welke gevolgen dit heeft voor de code.

4.16.1. Het nieuwe sjabloon

De structuur van het antwoord ziet er nu als volgt uit:

Dit sjabloon krijgt de naam modele2 en het script dat dit sjabloon genereert, krijgt de naam m-reponse2.php:

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

Het bijbehorende script voor dit sjabloon is als volgt:

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

De dynamische elementen van het sjabloon zijn de volgende:

  • $dReponse['urlstyle']: het te gebruiken stylesheet
  • $dReponse['vue1']: het script dat moet worden gebruikt om [vue1] te genereren
  • $dReponse['vue2']: het script dat moet worden gebruikt om [vue2] te genereren
  • $dReponse['vue3']: het script dat moet worden gebruikt om [vue3] te genereren

Deze elementen moeten door de makers van de weergaven worden vastgelegd.

4.16.2. De verschillende antwoordpagina's

De applicatie zal voortaan de volgende antwoorden aan de gebruiker presenteren. Bij de eerste aanroep zal de antwoordpagina er als volgt uitzien:

Als de gebruiker geldige gegevens invoert, wordt de belasting berekend:

Als hij fouten maakt bij het invoeren, wordt de foutpagina weergegeven:

Als hij de link [Retour au formulaire de saisie] gebruikt, krijgt hij het formulier terug zoals hij het heeft gevalideerd:

Als hij hierboven de link [Réinitialiser le formulaire] gebruikt, krijgt hij een leeg formulier te zien:

Merk op dat de applicatie wel degelijk dezelfde acties gebruikt als eerder. Alleen de weergave van de antwoorden is veranderd.

4.16.3. De basisweergaven

De basisweergave [vue1] wordt, net als in het vorige voorbeeld, gekoppeld aan het 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>  

Dit scherm bevat twee dynamische elementen:

  • $dReponse['titre']: weer te geven titel
  • $dReponse['resultat']: te betalen belastingbedrag

De elementaire weergave [vue2] wordt gekoppeld aan het volgende 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'].
        '">'.$dReponseQZXW2HTMLBWydsaWVucyddZQXQZXW2HTMLBWyRpXQZQXQZXW2HTMLBWyd0ZXh0ZSddZQX."</a></div></td></tr>\n";
    }//$i
  ?>
</table>

Dit overzicht bevat de volgende dynamische elementen:

  • $dReponse['liens']: tabel met links die moeten worden weergegeven in [vue2]. Elk element van de tabel is een woordenboek met twee sleutels:
    • 'url': doel-URL van de link
    • 'tekst': tekst van de link

De basisweergave [vue3] wordt gekoppeld aan het script v-formulaire2.php als het invoerformulier moet worden weergegeven, of aan het script v-erreurs2.php als de foutpagina moet worden weergegeven. De code van het script v-formulaire2.php is als volgt:

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

De dynamische onderdelen van deze weergave, die door de weergavegenerator moeten worden gedefinieerd, zijn gekoppeld aan de volgende sleutels in het woordenboek $dReponse:

  • optoui: status van de keuzeknop met de naam optoui
  • optnon: status van de keuzeknop met de naam optnon
  • kinderen: aantal kinderen dat in het veld txtenfants moet worden geplaatst
  • salaire: jaarsalaris dat in het veld txtsalaire moet worden geplaatst

Het script dat de foutpagina genereert, heet v-erreurs2.php. De code ervan is als volgt:

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

De dynamische onderdelen van deze weergave, die door de weergavegenerator moeten worden gedefinieerd, zijn gekoppeld aan de volgende sleutels in het woordenboek $dReponse:

  • fouten: tabel met foutmeldingen
  • info: informatiemelding

4.16.4. Het stylesheet

Dit is niet gewijzigd. Het is nog steeds style1.css.

4.16.5. Het nieuwe configuratiebestand

Om deze nieuwe weergaven te implementeren, moeten we enkele regels in het configuratiebestand aanpassen.

<?php

     // PHP-configuratie
  ini_set("register_globals","off");
  ini_set("display_errors","off");  
  ini_set("expose_php","off");

  // lijst met op te nemen modules
  $dConfig['includes']=array('c-impots-data.php','c-impots-calcul.php');

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

  // configuratie van de weergaven van de applicatie
  $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';  

  // configuratie van de acties van de applicatie
  $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');          

  // configuratie van de rapporten van de applicatie
  $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'));

  // configuratie van het applicatiemodel
    $dConfig["DSN"]=array(
        "sgbd"=>"mysql",
        "user"=>"seldbimpots",
        "mdp"=>"mdpseldbimpots",
        "host"=>"localhost",
        "database"=>"dbimpots"
    );
?>

De belangrijkste wijziging bestaat uit het aanpassen van de weergavegeneratoren die gekoppeld zijn aan de rapporten [e-formulaire] en [e-erreurs]. Zodra dit is gebeurd, worden de nieuwe weergavegeneratoren ingezet om de nieuwe responspagina's te genereren.

4.16.6. De weergavegenerator die is gekoppeld aan rapport [e-formulaire]

In het configuratiebestand is de rapportage [e-formulaire] nu gekoppeld aan de weergavegenerator e-formulaire2.php. De code van dit script is als volgt:

<?php
  // het formulierantwoord wordt voorbereid
  $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')
  );              

  // configuratie op basis van het te genereren formuliertype
    $type=$dSession['etat']['secondaire'];
  if($type=='init'){
      // leeg formulier
    $dReponse['optnon']='checked';
  }//if
  if($type=='calculimpot'){
      // moeten we de in de query opgeslagen invoerparameters opnieuw weergeven
    $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'){
      // moeten we de invoerparameters die in de sessie zijn opgeslagen opnieuw weergeven
    $dReponse['optoui']=$dSession['requete']['optmarie']=='oui' ? 'checked' : '';
    $dReponse['optnon']=$dReponse['optoui']=='' ? 'checked' : '';  
    $dReponse['enfants']=$dSession['requete']['txtenfants'];
    $dReponse['salaire']=$dSession['requete']['txtsalaire'];
  }//if
  // we sturen het antwoord
  finSession($dConfig,$dReponse,$dSession);
?>  

De belangrijkste wijzigingen zijn de volgende:

  • de weergavegenerator geeft aan dat hij het responsmodel modele2 wil gebruiken
  • daarom vult hij de dynamische elementen $dReponse['vue1'], $dReponse['vue2'], $dReponse['vue3'], die alle drie nodig zijn voor het responsmodel modele2.
  • De generator vult ook het dynamische element $dReponse['liens'] in, dat bepaalt welke koppelingen moeten worden weergegeven in het gebied [vue2] van het antwoord.

4.16.7. De weergavegenerator die is gekoppeld aan de rapportstatus [e-erreurs]

In het configuratiebestand is de rapport [e-erreurs] nu gekoppeld aan de weergavegenerator e-erreurs2.php. De code van dit script is als volgt:

<?php

  // we bereiden het foutantwoord voor
  $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')
  );              

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

  // het antwoord wordt verzonden
  finSession($dConfig,$dReponse,$dSession);
?>  

De aangebrachte wijzigingen zijn identiek aan die in de weergavegenerator e-formulaire2.php.

4.17. Conclusie

Aan de hand van een voorbeeld hebben we het nut van onze generieke controller kunnen aantonen. We hoefden deze niet zelf te schrijven. We hebben ons beperkt tot het schrijven van de scripts voor de acties, de weergavegeneratoren en de weergaven van de applicatie. Daarnaast hebben we het nut aangetoond van het scheiden van acties en weergaven. Zo konden we het uiterlijk van de antwoorden wijzigen zonder ook maar één regel code in de actiescripts aan te passen. Alleen de scripts die betrokken zijn bij het genereren van de weergaven zijn aangepast. Om dit mogelijk te maken, mag het actiescript geen aannames doen over de weergave waarin de door het script berekende informatie zal worden getoond. Het moet zich beperken tot het doorgeven van deze informatie aan de controller, die deze vervolgens doorgeeft aan de weergavegenerator, die de informatie vervolgens opmaakt. Dit is een absolute regel: een actie moet volledig los staan van de weergaven.

In dit hoofdstuk hebben we kennisgemaakt met de Struts-filosofie, die bij Java-ontwikkelaars welbekend is. Een ‘open source’-project met de naam php.mvc maakt het mogelijk om web-/PHP-ontwikkeling te doen volgens de Struts-filosofie. Ga naar de website http://www.phpmvc.net/ voor meer informatie.