4. Przykładowa aplikacja
Chcielibyśmy zilustrować powyższą metodę na przykładzie obliczania podatków.
4.1. Problem
Chcemy napisać program umożliwiający obliczenie podatku podatnika. Rozważamy uproszczony przypadek podatnika, który ma do zadeklarowania wyłącznie swoje wynagrodzenie:
- obliczamy liczbę udziałów pracownika nbParts=nbEnfants/2 +1, jeśli jest on osobą stanu wolnego, nbEnfants/2+2, jeśli jest żonaty, gdzie nbEnfants to liczba jego dzieci. Liczba udziałów zwiększa się o 0,5, jeśli ma troje lub więcej dzieci.
- oblicza się jego dochód podlegający opodatkowaniu R = 0,72 * S, gdzie S to jego roczne wynagrodzenie
- oblicza się jego współczynnik rodzinny Q = R/N
- oblicza się podatek I na podstawie następujących danych
| Każdy wiersz zawiera 3 pola graniczne: coeffR, coeffN. Aby obliczyć podatek I, należy znaleźć pierwszy wiersz, w którym QF ≤ wartość graniczna. Na przykład, jeśli QF = 30000, znajdziemy wiersz: 31810 0,2 3309,5. Podatek I wynosi wówczas 0,2*R – 3309,5*nbParts. Jeśli QF jest takie, że relacja QF <= limit nigdy nie jest spełniona, wówczas stosuje się współczynniki z ostatniego wiersza: 0 0,65 49062, co daje podatek I = 0,65*R – 49062*nbParts. |
4.2. Baza danych
Powyższe dane są zapisane w bazie o nazwie MySQL, zwanej dbimpots. Użytkownik seldbimpots z hasłem mdpseldbimpots ma dostęp tylko do odczytu do zawartości bazy. Baza ta zawiera jedną tabelę o nazwie impots, której struktura jest następująca:

Jej zawartość jest następująca:

4.3. Architektura aplikacji MVC
Aplikacja będzie miała następującą architekturę MVC:
![]() |
- kontroler main.php będzie kontrolerem ogólnym przedstawionym wcześniej
- Żądanie klienta jest wysyłane do kontrolera w postaci zapytania o postaci main.php?action=xx. Wartość parametru „action” określa skrypt bloku ACTIONS, który ma zostać wykonany. Wykonany skrypt akcji zwraca do kontrolera zmienną wskazującą stan, w jakim należy ustawić aplikację internetową. Na podstawie tego stanu kontroler uruchamia jeden z generatorów widoków, aby wysłać odpowiedź do klienta.
- impots-data.php to klasa odpowiedzialna za dostarczanie kontrolerowi potrzebnych danych
- impots-calcul.php to klasa biznesowa umożliwiająca obliczenie podatku
4.4. Klasa dostępu do danych
Klasa dostępu do danych została zaprojektowana tak, aby ukryć przed aplikacją internetową pochodzenie danych. W jej interfejsie znajduje się metoda getData, która dostarcza trzy tablice danych niezbędne do obliczenia podatku. W naszym przykładzie dane są pobierane z bazy danych MySQL. Aby klasa była niezależna od rzeczywistego typu SGBD, wykorzystamy bibliotekę pear::DB opisaną w załączniku. Kod klasy wygląda następująco:
<?php
// biblioteki
require_once 'DB.php';
class impots_data{
// klasa dostępu do źródła danych DBIMPOTS
// atrybuty
var $sDSN; // ciąg połączenia
var $sDatabase; // nazwa bazy danych
var $oDB; // połączenie z bazą danych
var $aErreurs; // lista błędów
var $oRésultats; // wynik zapytania
var $connecté; // wartość logiczna wskazująca, czy nawiązano połączenie z bazą danych
var $sQuery; // ostatnie wykonane zapytanie
// konstruktor
function impots_data($dDSN){
// $dDSN: słownik definiujący połączenie, które ma zostać nawiązane
// $dDSN['sgbd']: typ obiektu SGBD, z którym należy się połączyć
// $dDSN['host']: nazwa komputera hosta, na którym się znajduje
// $dDSN['database']: nazwa bazy danych, z którą należy się połączyć
// $dDSN['user']: użytkownik bazy danych
// $dDSN['mdp']: jego hasło
// tworzy w $oDB połączenie z bazą zdefiniowaną przez $dDSN pod tożsamością $dDSN['user']
// jeśli połączenie się powiedzie
// zapisuje w $sDSN ciąg połączenia z bazą danych
// zapisuje w zmiennej $sDataBase nazwę bazy danych, z którą nawiązano połączenie
// ustawia $connecté na wartość true
// jeśli połączenie się nie powiedzie
// umieszcza odpowiednie komunikaty o błędach na liście $aErreurs
// zamyka połączenie, jeśli zajdzie taka potrzeba
// ustawia $connecté na „false”
// kasuje listę błędów
$this->aErreurs=array();
// tworzy się połączenie z bazą danych $sDSN
$this->sDSN=$dDSN["sgbd"]."://".$dDSN["user"].":".$dDSN["mdp"]."@".$dDSN["host"]."/".$dDSN["database"];
$this->sDatabase=$dDSN["database"];
$this->connect();
// połączono?
if( ! $this->connecté) return;
// połączenie nawiązano
$this->connecté=TRUE;
}//producent
// ------------------------------------------------------------------
function connect(){
// (ponowne) połączenie z bazą danych
// wyzerowanie listy błędów
$this->aErreurs=array();
// tworzy się połączenie z bazą danych $sDSN
$this->oDB=DB::connect($this->sDSN,true);
// błąd?
if(DB::iserror($this->oDB)){
// rejestruje się błąd
$this->aErreurs[]="Echec de la connexion à la base [".$this->sDatabase."] : [".$this->oDB->getMessage()."]";
// połączenie nie powiodło się
$this->connecté=FALSE;
// koniec
return;
}
// połączenie nawiązane
$this->connecté=TRUE;
}//połączenie
// ------------------------------------------------------------------
function disconnect(){
// jeśli połączenie jest aktywne, zamykamy połączenie z bazą $sDSN
if($this->connecté){
$this->oDB->disconnect();
// jesteśmy odłączeni
$this->connecté=FALSE;
}//if
}//rozłącz
// -------------------------------------------------------------------
function execute($sQuery){
// $sQuery: zapytanie do wykonania
// zapisujemy zapytanie
$this->sQuery=$sQuery;
// czy jesteśmy zalogowani?
if(! $this->connecté){
// odnotowuje się błąd
$this->aErreurs[]="Pas de connexion existante à la base [$this->sDatabase]";
// koniec
return;
}//if
// wykonanie zapytania
$this->oRésultats=$this->oDB->query($sQuery);
// błąd?
if(DB::iserror($this->oRésultats)){
// odnotowano błąd
$this->aErreurs[]="Echec de la requête [$sQuery] : [".$this->oRésultats->getMessage()."]";
// powrót
return;
}//if
}//wykonaj
// ------------------------------------------------------------------
function getData(){
// pobieramy 3 serie danych: limity, współczynniki, współczynniki n
$this->execute('select limites, coeffR, coeffN from impots');
// czy są błędy?
if(count($this->aErreurs)!=0) return array();
// przeglądamy wynik zapytania 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
}//klasa
?>
Program testowy mógłby wyglądać następująco:
<?php
// biblioteka
require_once "c-impots-data.php";
require_once "DB.php";
// test klasy impots-data
ini_set('track_errors','on');
ini_set('display_errors','on');
// konfiguracja bazy danych dbimpots
$dDSN=array(
"sgbd"=>"mysql",
"user"=>"seldbimpots",
"mdp"=>"mdpseldbimpots",
"host"=>"localhost",
"database"=>"dbimpots"
);
// logowanie
$oImpots=new impots_data($dDSN);
// błędy?
if(checkErreurs($oImpots)){
exit(0);
}
// monitorowanie
echo "Connecté à la base...\n";
// pobieranie danych dotyczących limitów, współczynników coeffr i coeffn
list($limites,$coeffr,$coeffn)=$oImpots->getData();
// błędy?
if( ! checkErreurs($oImpots)){
// treść
echo "données : \n";
for($i=0;$i<count($limites);$i++){
echo "[$limites[$i],$coeffr[$i],$coeffn[$i]]\n";
}//for
}//if
// rozłączamy się
$oImpots->disconnect();
// śledzenie
echo "Déconnecté de la base...\n";
// koniec
exit(0);
// ----------------------------------
function checkErreurs(&$oImpots){
// czy są jakieś błędy?
if(count($oImpots->aErreurs)!=0){
// wyświetlanie
for($i=0;$i<count($oImpots->aErreurs);$i++){
echo $oImpots->aErreurs[$i]."\n";
}//dla
// błędów
return true;
}//if
// brak błędów
return false;
}//checkErreurs
?>
Uruchomienie tego programu testowego daje następujące wyniki:
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. Klasa służąca do obliczania podatku
Klasa ta służy do obliczania podatku podatnika. Do jej konstruktora przekazywane są dane niezbędne do wykonania obliczeń. Następnie klasa ta oblicza odpowiedni podatek. Kod klasy jest następujący:
<?php
class impots_calcul{
// klasa obliczania podatku
// konstruktor
function impots_calcul(&$perso,&$data){
// $perso: słownik z następującymi kluczami
// dzieci: liczba dzieci
// wynagrodzenie: roczne wynagrodzenie
// marié(e): wartość logiczna wskazująca, czy podatnik jest w związku małżeńskim, czy nie
// podatek(i): podatek do zapłaty obliczony przez tę funkcję
// $data: słownik zawierający następujące klucze
// limity: tabela limitów przedziałów podatkowych
// współczynniki: tabela współczynników dochodu
// współczynniki: tabela współczynników liczby udziałów
// wszystkie 3 tabele mają taką samą liczbę elementów
// obliczenie liczby udziałów
if($perso['marié'])
$nbParts=$perso['enfants']/2+2;
else $nbParts=$perso['enfants']/2+1;
if ($perso['enfants']>=3) $nbParts+=0.5;
// dochód podlegający opodatkowaniu
$revenu=0.72*$perso['salaire'];
// współczynnik rodzinny
$QF=$revenu/$nbParts;
// wyszukiwanie przedziału podatkowego odpowiadającego QF
$nbTranches=count($data['limites']);
$i=0;
while($i<$nbTranches-2 && $QF>$data['limites'][$i]) $i++;
// podatek
$perso['impot']=floor($data['coeffr'][$i]*$revenu-$data['coeffn'][$i]*$nbParts);
}//deweloper
}//klasa
?>
Program testowy mógłby wyglądać następująco:
<?php
// biblioteka
require_once "c-impots-data.php";
require_once "c-impots-calcul.php";
// konfiguracja bazy dbimpots
$dDSN=array(
"sgbd"=>"mysql",
"user"=>"seldbimpots",
"mdp"=>"mdpseldbimpots",
"host"=>"localhost",
"database"=>"dbimpots"
);
// logowanie
$oImpots=new impots_data($dDSN);
// błędy?
if(checkErreurs($oImpots)){
exit(0);
}
// monitorowanie
echo "Connecté à la base...\n";
// pobieranie danych dotyczących limitów, współczynników coeffr i coeffn
list($limites,$coeffr,$coeffn)=$oImpots->getData();
// błędy?
if(checkErreurs($oImpots)){
exit(0);
}
// rozłączamy się
$oImpots->disconnect();
// śledzenie
echo "Déconnecté de la base...\n";
// obliczanie podatku
$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);
// koniec
exit(0);
// ----------------------------------
function checkErreurs(&$oImpots){
// błędy?
if(count($oImpots->aErreurs)!=0){
// wyświetlanie
for($i=0;$i<count($oImpots->aErreurs);$i++){
echo $oImpots->aErreurs[$i]."\n";
}//dla
// błędy
return true;
}//if
// brak błędów
return false;
}//checkErreurs
?>
Uruchomienie tego programu testowego daje następujące wyniki:
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. Działanie aplikacji
Po uruchomieniu internetowej aplikacji do obliczania podatku wyświetla się następujący ekran [v-formulaire]:
![]() |
Użytkownik wypełnia pola i zleca obliczenie podatku:
![]() |
Warto zauważyć, że formularz jest odtwarzany w stanie, w jakim został zatwierdzony przez użytkownika, a ponadto wyświetla kwotę podatku do zapłaty. Użytkownik może popełnić błędy podczas wprowadzania danych. Są one sygnalizowane na stronie błędów, którą nazwiemy widokiem [v-erreurs].
![]() |
![]() |
Link [Retour au formulaire de saisie] umożliwia użytkownikowi wyświetlenie formularza w postaci, w jakiej został zatwierdzony.
![]() |
Wreszcie przycisk [Effacer le formulaire] przywraca formularz do stanu początkowego, c.a.d, w jakim użytkownik go otrzymał podczas pierwotnego żądania.
4.7. Omówienie architektury aplikacji MVC
Aplikacja ma następującą architekturę MVC:
![]() |
Właśnie opisaliśmy dwie klasy: impots-data.php i impots-calcul.php. Teraz omówimy pozostałe elementy architektury.
4.8. Kontroler aplikacji
Kontroler aplikacji main.php to ten, który został opisany w pierwszej części tego rozdziału. Jest to kontroler ogólny, niezależny od aplikacji.
<?php
// kontroler ogólny
// odczyt konfiguracji
include 'config.php';
// dołączanie bibliotek
for($i=0;$i<count($dConfig['includes']);$i++){
include($dConfig['includes'][$i]);
}//for
// rozpoczynamy lub wznawiamy sesję
session_start();
$dSession=$_SESSION["session"];
if($dSession) $dSession=unserialize($dSession);
// pobieranie akcji do wykonania
$sAction=$_GET['action'] ? strtolower($_GET['action']) : 'init';
$sAction=strtolower($_SERVER['REQUEST_METHOD']).":$sAction";
// czy sekwencja działań przebiega prawidłowo?
if( ! enchainementOK($dConfig,$dSession,$sAction)){
// nienormalna sekwencja
$sAction='enchainementinvalide';
}//if
// przetwarzanie akcji
$scriptAction=$dConfig['actions'][$sAction] ?
$dConfig['actions'][$sAction]['url'] :
$dConfig['actions']['actionInvalide']['url'];
include $scriptAction;
// wysłanie odpowiedzi (widoku) do klienta
$sEtat=$dSession['etat']['principal'];
$scriptVue=$dConfig['etats'][$sEtat]['vue'];
include $scriptVue;
// koniec skryptu – nie powinno się tu dotrzeć, chyba że występuje błąd
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: słownik konfiguracyjny
// $dSession: słownik zawierający informacje o sesji
// $dReponse: słownik argumentów strony odpowiedzi
// zapis sesji
if(isset($dSession)){
// parametry żądania są umieszczane w sesji
$dSession['requete']=strtolower($_SERVER['REQUEST_METHOD'])=='get' ? $_GET :
strtolower($_SERVER['REQUEST_METHOD'])=='post' ? $_POST : array();
$_SESSION['session']=serialize($dSession);
session_write_close();
}else{
// brak sesji
session_destroy();
}
// wyświetlanie odpowiedzi
include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];
// koniec skryptu
exit(0);
}//zakończenie sesji
//--------------------------------------------------------------------
function enchainementOK(&$dConfig,&$dSession,$sAction){
// sprawdza, czy bieżąca akcja jest dozwolona w odniesieniu do poprzedniego stanu
$etat=$dSession['etat']['principal'];
if(! isset($etat)) $etat='sansetat';
// weryfikacja akcji
$actionsautorisees=$dConfig['etats'][$etat]['actionsautorisees'];
$autorise= ! isset($actionsautorisees) || in_array($sAction,$actionsautorisees);
return $autorise;
}
//--------------------------------------------------------------------
function dump($dInfos){
// wyświetla słownik informacji
while(list($clé,$valeur)=each($dInfos)){
echo "[$clé,$valeur]<br>\n";
}//while
}//monitorowanie
//--------------------------------------------------------------------
function trace($msg){
echo $msg."<br>\n";
}//monitorowanie
?>
4.9. Akcje w aplikacji internetowej
Istnieją cztery akcje:
- get:init: jest to akcja uruchamiana podczas pierwszego żądania skierowanego do kontrolera bez parametrów. Generuje ona pusty widok „formularza”.
- post:effacerformulaire: akcja uruchamiana przez przycisk [Effacer le formulaire]. Generuje pusty widok [v-formulaire].
- post:calculerimpot: akcja uruchamiana przez przycisk [Calculer l'impôt]. Generuje ona albo widok [v-formulaire] z kwotą podatku do zapłaty, albo widok [v-erreurs].
- get:retourformulaire: akcja uruchamiana przez link [Retour au formulaire de saisie]. Generuje widok [v-formulaire] wstępnie wypełniony błędnymi danymi.
Działania te są skonfigurowane w następujący sposób w pliku konfiguracyjnym:
<?php
…
// konfiguracja działań aplikacji
$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');
Każdej akcji przypisany jest skrypt odpowiedzialny za jej przetworzenie. Każda akcja przenosi aplikację internetową do stanu określonego przez element $dSession['etat']['principal']. Stan ten ma zostać zapisany w sesji. Ponadto akcja zapisuje w słowniku $dReponse informacje niezbędne do wyświetlenia widoku powiązanego z nowym stanem, w którym znajdzie się aplikacja.
4.10. Raporty aplikacji internetowej
Są dwa:
- [e-formulaire]: stan, w którym prezentowane są różne warianty widoku [v-formulaire].
- [e-erreurs]: stan, w którym wyświetlany jest widok [v-erreurs].
Działania dozwolone w tych stanach są następujące:
<?php
…
// konfiguracja stanów aplikacji
$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'));
W danym stanie dozwolone działania odpowiadają celom linków lub przycisków [submit] w widoku powiązanym z tym stanem. Ponadto działanie „get:init” jest zawsze dozwolone. Pozwala to użytkownikowi na pobranie URL main.php z listy URL w przeglądarce i ponowne odtworzenie go niezależnie od stanu aplikacji. Jest to swego rodzaju „ręczne” zresetowanie. Stan „bezstanu” występuje wyłącznie podczas uruchamiania aplikacji.
Z każdym stanem aplikacji powiązany jest skrypt odpowiedzialny za wygenerowanie widoku odpowiadającego temu stanowi:
- stan [e-formulaire]: skrypt e-formulaire.php
- stan [e-erreurs]: skrypt e-erreurs.php
Raport [e-formulaire] będzie zawierał widok [v-formulaire] wraz z wariantami. Widok [v-formulaire] może bowiem być wyświetlany jako pusty, wstępnie wypełniony lub z kwotą podatku. Czynność, która spowoduje przejście aplikacji do stanu [e-formulaire], określa w zmiennej $dSession['etat']['principal'] główny stan aplikacji. Kontroler wykorzystuje wyłącznie tę informację. W naszej aplikacji akcja, która prowadzi do stanu [e-formulaire], doda do $dSession['etat']['secondaire'] dodatkową informację, dzięki której generator odpowiedzi będzie wiedział, czynależy wygenerować pusty formularz, formularz wstępnie wypełniony, z kwotą podatku lub bez niej. Można było postąpić inaczej, uznając, że mamy tu do czynienia z trzema różnymi stanami, a zatem z trzema generatorami widoku, które należałoby napisać.
4.11. Plik konfiguracyjny aplikacji internetowej config.php
<?php
// konfiguracja PHP
ini_set("register_globals","off");
ini_set("display_errors","off");
ini_set("expose_php","off");
// lista modułów do dołączenia
$dConfig['includes']=array('c-impots-data.php','c-impots-calcul.php');
// kontroler aplikacji
$dConfig['webapp']=array('titre'=>"Calculez votre impôt");
// konfiguracja widoków aplikacji
$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';
// konfiguracja akcji aplikacji
$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');
// konfiguracja stanów aplikacji
$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'));
// konfiguracja szablonu aplikacji
$dConfig["DSN"]=array(
"sgbd"=>"mysql",
"user"=>"seldbimpots",
"mdp"=>"mdpseldbimpots",
"host"=>"localhost",
"database"=>"dbimpots"
);
?>
4.12. Działania aplikacji internetowej
4.12.1. Ogólne działanie skryptów akcji
- Skrypt akcji jest wywoływany przez kontroler na podstawie parametru „action”, który otrzymał od klienta.
- Po wykonaniu skrypt akcji musi wskazać kontrolerowi stan, w jakim ma zostać ustawiona aplikacja. Stan ten należy wskazać w $dSession['etat']['principal'].
- Skrypt akcji może chcieć umieścić informacje w sesji. Czyni to, umieszczając je w słowniku $dSession, który jest automatycznie zapisywany w sesji przez kontroler pod koniec cyklu żądanie-odpowiedź.
- Skrypt akcji może zawierać informacje, które należy przekazać do widoków. Ten aspekt jest niezależny od kontrolera. Jest to interfejs między akcjami a widokami, specyficzny dla każdej aplikacji. W omawianym tutaj przykładzie akcje będą dostarczać informacje do generatorów widoków za pośrednictwem słownika o nazwie $dReponse.
4.12.2. Akcja get:init
Jest to akcja, która generuje pusty formularz. Plik konfiguracyjny wskazuje, że zostanie ona przetworzona przez skrypt a-init.php:
Kod skryptu a-init.php wygląda następująco:
<?php
// wyświetla się formularz wprowadzania danych
$dSession['etat']=array('principal'=>'e-formulaire', 'secondaire'=>'init');
?>
Ten skrypt ogranicza się do ustawienia w $dSession['etat']['principal'] stanu, w jakim powinna znajdować się aplikacja, stan [e-formulaire], a w pliku $dSession['etat']['secondaire'] podaje szczegółowe informacje na temat tego stanu. Plik konfiguracyjny wskazuje, że kontroler uruchomi skrypt e-formulaire.php w celu wygenerowania odpowiedzi dla klienta.
<?php
…
$dConfig['etats']['e-formulaire']=array(
'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
'vue'=>'e-formulaire.php');
Skrypt e-formulaire.php wygeneruje widok [v-formulaire] w jego wariantach [init] i c.a.d – pusty formularz.
4.12.3. Akcja post:calculerimpot
Jest to akcja umożliwiająca obliczenie podatku na podstawie danych wprowadzonych w formularzu. Plik konfiguracyjny wskazuje, że akcję tę przetworzy skrypt a-calculimpot.php:
Kod skryptu a-calculimpot.php jest następujący:
<?php
// wniosek o obliczenie podatku
// najpierw sprawdzana jest poprawność parametrów
$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é";
}
// jeśli występują błędy, proces się kończy
if(count($erreurs)!=0){
// przygotowanie strony błędów
$dReponse['erreurs']=&$erreurs;
$dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'saisie');
return;
}//if
// wprowadzone dane są poprawne
// pobieramy dane niezbędne do obliczenia podatku
if(! $dSession['limites']){
// dane nie znajdują się w sesji
// pobierane są one ze źródła danych
list($erreurs,$limites,$coeffr,$coeffn)=getData($dConfig['DSN']);
// jeśli występują błędy, wyświetlana jest strona błędów
if(count($erreurs)!=0){
// przygotowanie strony błędów
$dReponse['erreurs']=&$erreurs;
$dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'database');
return;
}//if
// brak błędów – dane są zapisywane w sesji
$dSession['limites']=&$limites;
$dSession['coeffr']=&$coeffr;
$dSession['coeffn']=&$coeffn;
}//if
// tutaj mamy dane niezbędne do obliczenia podatku
// obliczamy podatek
$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);
// przygotowanie strony odpowiedzi
$dSession['etat']=array('principal'=>'e-formulaire','secondaire'=>'calculimpot');
$dReponse['impot']=$dPerso['impot'];
return;
//-----------------------------------------------------------------------
function getData($dDSN){
// połączenie ze źródłem danych zdefiniowanym w słowniku $dDSN
$oImpots=new impots_data($dDSN);
if(count($oImpots->aErreurs)!=0) return array($oImpots->aErreurs);
// pobieranie danych limitów, współczynnika, współczynnika n
list($limites,$coeffr,$coeffn)=$oImpots->getData();
// rozłączamy się
$oImpots->disconnect();
// zwracanie wyniku
if(count($oImpots->aErreurs)!=0) return array($oImpots->aErreurs);
else return array(array(),$limites,$coeffr,$coeffn);
}//getData
Skrypt wykonuje swoje zadanie: oblicza podatek. Rozszyfrowanie kodu przetwarzania pozostawiamy czytelnikowi. Interesują nas stany, które mogą wystąpić w wyniku tej operacji:
- wprowadzone dane są nieprawidłowe lub występują problemy z dostępem do danych: aplikacja przechodzi w stan [e-erreurs]. Plik konfiguracyjny wskazuje, że za wygenerowanie widoku odpowiedzi odpowiedzialny będzie skrypt e-erreurs.php:
<?php
…
$dConfig['etats']['e-erreurs']=array(
'actionsautorisees'=>array('get:retourformulaire','get:init'),
'vue'=>'e-erreurs.php');
- we wszystkich pozostałych przypadkach aplikacja przechodzi do stanu [e-formulaire] z wariantem calculimpot wskazanym w $dSession['etat']['secondaire']. Plik konfiguracyjny wskazuje, że to skrypt e-formulaire.php wygeneruje widok odpowiedzi. Skrypt ten wykorzysta wartość z pliku $dSession['etat']['secondaire'] do wygenerowania formularza wstępnie wypełnionego wartościami wprowadzonymi przez użytkownika oraz kwotą podatku.
4.12.4. Akcja post:effacerformulaire
Jest ona powiązana w konfiguracji ze skryptem a-init.php, który został już opisany.
4.12.5. Akcja get:powrót_do_formularza
Umożliwia powrót do stanu [e-formulaire] ze stanu [e-erreurs]. Akcję tę obsługuje skrypt a-retourformulaire.php:
Skrypt a-retourformulaire.php ma następującą treść:
<?php
// wyświetlanie formularza wprowadzania danych
$dSession['etat']=array('principal'=>'e-formulaire','secondaire'=>'retourformulaire');
?>
Wystarczy po prostu ustawić aplikację w stanie [e-formulaire] w jej wariancie [retourformulaire]. Plik konfiguracyjny wskazuje, że kontroler uruchomi skrypt e-formulaire.php w celu wygenerowania odpowiedzi dla klienta.
<?php
…
$dConfig['etats']['e-formulaire']=array(
'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
'vue'=>'e-formulaire.php');
Skrypt e-formulaire.php wygeneruje widok [v-formulaire] w jego wariantach [retourformulaire] i c.a.d. formularz wstępnie wypełniony wartościami wprowadzonymi przez użytkownika, ale bez kwoty podatku.
4.13. Nieprawidłowa sekwencja działań
Dopuszczalne działania w danym stanie aplikacji są określone w konfiguracji:
<?php
…
// konfiguracja stanów aplikacji
$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'));
Wyjaśniliśmy już tę konfigurację. W przypadku wykrycia nieprawidłowej sekwencji działań uruchamiany jest skrypt a-enchainementinvalide.php:
Kod tego skryptu wygląda następująco:
<?php
// nieprawidłowa sekwencja działań
$dReponse['erreurs']=array("Enchaînement d'actions invalide");
$dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'enchainementinvalide');
?>
Polega to na ustawieniu aplikacji w stanie [e-erreurs]. W stanach $dSession, ['etat'] i ['secondaire'] podajemy informacje, które zostaną wykorzystane przez generator strony błędów. Jak już widzieliśmy, generatorem tym jest e-erreurs.php:
<?php
…
$dConfig['etats']['e-erreurs']=array(
'actionsautorisees'=>array('get:retourformulaire','get:init'),
'vue'=>'e-erreurs.php');
Kod tego generatora omówimy później. Widok wysyłany do klienta wygląda następująco:

4.14. Widoki aplikacji
4.14.1. Wyświetlanie widoku końcowego
Przyjrzyjmy się, w jaki sposób kontroler wysyła odpowiedź do klienta po wykonaniu akcji, o którą ten poprosił:
<?php
…
....
// rozpoczęcie lub wznowienie sesji
session_start();
$dSession=$_SESSION["session"];
if($dSession) $dSession=unserialize($dSession);
// pobieranie akcji do wykonania
$sAction=$_GET['action'] ? strtolower($_GET['action']) : 'init';
$sAction=strtolower($_SERVER['REQUEST_METHOD']).":$sAction";
// czy sekwencja działań jest prawidłowa?
if( ! enchainementOK($dConfig,$dSession,$sAction)){
// nieprawidłowa sekwencja
$sAction='enchainementinvalide';
}//if
// przetwarzanie akcji
$scriptAction=$dConfig['actions'][$sAction] ?
$dConfig['actions'][$sAction]['url'] :
$dConfig['actions']['actionInvalide']['url'];
include $scriptAction;
// wysłanie odpowiedzi (widoku) do klienta
$sEtat=$dSession['etat']['principal'];
$scriptVue=$dConfig['etats'][$sEtat]['vue'];
include $scriptVue;
.....
// ---------------------------------------------------------------
function finSession(&$dConfig,&$dReponse,&$dSession){
// $dConfig: słownik konfiguracyjny
// $dSession: słownik zawierający informacje o sesji
// $dReponse: słownik argumentów strony odpowiedzi
// zapis sesji
...
// wysłanie odpowiedzi do klienta
include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];
// koniec skryptu
exit(0);
}//zakończenie sesji
Po powrocie ze skryptu akcji kontroler pobiera z plików $dSession, ['etat'] oraz ['principal'] stan, w jaki ma przełączyć aplikację. Stan ten został ustalony przez właśnie wykonaną akcję. Następnie kontroler uruchamia generator widoku powiązanego z tym stanem. Nazwę tego widoku odnajduje w pliku konfiguracyjnym. Rola generatora widoku polega na:
- ustala w pliku $dReponse['vuereponse'] nazwę szablonu odpowiedzi, który ma zostać użyty. Informacja ta zostanie przekazana do kontrolera. Szablon jest kompozycją podstawowych widoków, które po połączeniu tworzą widok końcowy.
- przygotowuje dynamiczne informacje, które mają zostać wyświetlone w widoku końcowym. Ten etap jest niezależny od kontrolera. Stanowi on interfejs między generatorem widoków a widokiem końcowym. Jest on specyficzny dla każdej aplikacji.
- musi kończyć się wywołaniem funkcji finSession kontrolera. Funkcja ta
- zapisze sesję
- wysłać odpowiedź
Kod funkcji finSession jest następujący:
<?php
…
// ---------------------------------------------------------------
function finSession(&$dConfig,&$dReponse,&$dSession){
// $dConfig: słownik konfiguracyjny
// $dSession: słownik zawierający informacje o sesji
// $dReponse: słownik argumentów strony odpowiedzi
// zapis sesji
...
// wysłanie odpowiedzi do klienta
include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];
// koniec skryptu
exit(0);
}//zakończenie sesji
Widok wysyłany do użytkownika jest definiowany przez encję $dReponse['vuereponse'], która określa szablon, jaki ma zostać użyty w ostatecznej odpowiedzi.
4.14.2. Szablon odpowiedzi
Aplikacja wygeneruje różne odpowiedzi zgodnie z następującym, jednolitym szablonem:
![]() |
Szablon ten jest powiązany z kluczem modele1 w słowniku $dConfig['vuesreponse']:
Skrypt m-reponse.php odpowiada za wygenerowanie tego szablonu:
<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>
Ten skrypt zawiera trzy elementy dynamiczne umieszczone w słowniku $dReponse i powiązane z następującymi kluczami:
- urlstyle: adres URL arkusza stylów szablonu
- vue1: nazwa skryptu odpowiedzialnego za wygenerowanie widoku vue1
- vue2: nazwa skryptu odpowiedzialnego za wygenerowanie widoku vue2
Generator widoku, który chce korzystać z modelu modèle1, musi zdefiniować te trzy elementy dynamiczne. Teraz zdefiniujemy widoki elementarne, które mogą zastąpić elementy [vue1] i [vue2] w modelu.
4.14.3. Widok podstawowy v-bandeau.php
Skrypt v-bandeau.php generuje widok, który zostanie umieszczony w obszarze [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>
Generator widoku powinien zdefiniować dwa elementy dynamiczne umieszczone w słowniku $dReponse i powiązane z następującymi kluczami:
- tytuł: tytuł do wyświetlenia
- wynik: kwota podatku do zapłaty
4.14.4. Widok podstawowy v-formulaire.php
Część [vue2] odpowiada albo widokowi [v-formulaire], albo widokowi [v-erreurs]. Widok [v-formulaire] jest generowany przez skrypt 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>
Dynamiczne elementy tego widoku, które muszą zostać zdefiniowane przez generator widoku, są powiązane z następującymi kluczami słownika $dReponse:
- optoui: stan przycisku opcji o nazwie optoui
- optnon: stan przycisku opcji o nazwie optnon
- dzieci: liczba dzieci, którą należy wpisać w polu txtenfants
- wynagrodzenie: roczne wynagrodzenie, które należy umieścić w polu txtsalaire
4.14.5. Widok podstawowy v-erreurs.php
Widok [v-erreurs] jest generowany przez skrypt 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>
Dynamiczne elementy tego widoku, które należy zdefiniować za pomocą generatora widoków, są powiązane z następującymi kluczami słownika $dReponse:
- błędy: tablica komunikatów o błędach
- info: komunikat informacyjny
- link: tekst linku
- href: adres URL docelowy powyższego linku
4.14.6. Arkusz stylów
Wszystkie widoki są „formatowane” za pomocą arkusza stylów. Aby zmienić wygląd aplikacji, należy zmodyfikować jej arkusz stylów. Poniższy arkusz stylów 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. Generatory widoków
4.15.1. Rola generatora widoku
Przypomnijmy sekwencję kodu kontrolera, która uruchamia generator widoku:
<?php
…
// przetwarzanie akcji
$scriptAction=$dConfig['actions'][$sAction] ?
$dConfig['actions'][$sAction]['url'] :
$dConfig['actions']['actionInvalide']['url'];
include $scriptAction;
// wysyłanie odpowiedzi (widoku) do klienta
$sEtat=$dSession['etat']['principal'];
$scriptVue=$dConfig['etats'][$sEtat]['vue'];
include $scriptVue;
Generator widoku jest powiązany ze stanem, w którym zostanie umieszczona aplikacja. Powiązanie między stanem a generatorem widoku ustala się poprzez konfigurację:
<?php
…
// konfiguracja stanów aplikacji
$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'));
Wspomnieliśmy już, jaką rolę pełni generator widoku. Przypomnijmy to tutaj. Generator widoku:
- określa w pliku $dReponse['vuereponse'] nazwę szablonu odpowiedzi, który ma zostać użyty. Informacja ta zostanie przekazana do kontrolera. Szablon to zbiór podstawowych widoków, które po połączeniu tworzą ostateczny widok.
- przygotowuje dynamiczne informacje, które mają zostać wyświetlone w widoku końcowym. Ten element jest niezależny od kontrolera. Stanowi on interfejs między generatorem widoków a widokiem końcowym. Jest on specyficzny dla każdej aplikacji.
- musi kończyć się wywołaniem funkcji finSession kontrolera. Funkcja ta
- zapisze sesję
- wysłać odpowiedź
4.15.2. Generator widoku powiązany ze stanem [e-formulaire]
Skrypt odpowiedzialny za generowanie widoku powiązanego z raportem [e-formulaire] nosi nazwę e-formulaire.php:
<?php
…
$dConfig['etats']['e-formulaire']=array(
'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
'vue'=>'e-formulaire.php');
Jego kod wygląda następująco:
<?php
// przygotowywanie odpowiedzi w formie formularza
$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'];
// konfiguracja w zależności od typu generowanego formularza
$type=$dSession['etat']['secondaire'];
if($type=='init'){
// pusty formularz
$dReponse['optnon']='checked';
}//if
if($type=='calculimpot'){
// należy ponownie wyświetlić parametry wprowadzania danych zapisane w zapytaniu
$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'){
// należy ponownie wyświetlić parametry wprowadzania danych zapisane w sesji
$dReponse['optoui']=$dSession['requete']['optmarie']=='oui' ? 'checked' : '';
$dReponse['optnon']=$dReponse['optoui']=='' ? 'checked' : '';
$dReponse['enfants']=$dSession['requete']['txtenfants'];
$dReponse['salaire']=$dSession['requete']['txtsalaire'];
}//if
// wysyłamy odpowiedź
finSession($dConfig,$dReponse,$dSession);
?>
Należy zauważyć, że generator widoku spełnia warunki nałożone na generator widoku:
- określa w pliku $dReponse['vuereponse'] szablon odpowiedzi, który ma być użyty
- przekazać informacje do tego szablonu. W tym przypadku są one przekazywane za pośrednictwem słownika $dReponse.
- zakończenie wywołaniem funkcji finSession kontrolera
W tym przypadku używanym szablonem jest „modèle1”. Generator definiuje zatem dwie informacje potrzebne temu szablonowi: $dReponse['vue1'] oraz $dReponse['vue2'].
W konkretnym przypadku naszej aplikacji widok powiązany ze stanem [e-formulaire] zależy od informacji przechowywanej w zmiennej $dSession['etat']['secondaire']. Jest to wybór programistyczny. Inna aplikacja mogłaby zdecydować się na przekazywanie dodatkowych informacji w inny sposób. Ponadto w tym przypadku wszystkie informacje niezbędne do wyświetlenia widoku końcowego są umieszczone w słowniku $dReponse. Również w tym przypadku jest to wybór należący do programisty. Raport [e-formulaire] może zostać wygenerowany po wykonaniu czterech różnych akcji: init, calculateTax, returnForm, clearForm. Widok, który ma zostać wyświetlony, nie jest dokładnie taki sam we wszystkich przypadkach. Dlatego w $dSession['etat']['secondaire'] wyróżniono trzy przypadki:
- init: formularz jest wyświetlany jako pusty
- obliczPodatek: formularz jest wyświetlany z kwotą podatku oraz danymi, na podstawie których został on obliczony
- retourformulaire: formularz jest wyświetlany z danymi wprowadzonymi początkowo
Powyższy skrypt e-formulaire.php wykorzystuje te informacje do wyświetlenia odpowiedzi zgodnie z tymi trzema wariantami.
4.15.3. Widok powiązany z raportem [e-erreurs]
Skrypt odpowiedzialny za generowanie widoku powiązanego z raportem [e-erreurs] nosi nazwę e-erreurs.php i ma następującą treść:
<?php
// przygotowujemy odpowiedź na błędy
$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';
// dodatkowe informacje
$type=$dSession['etat']['secondaire'];
if($type=='database'){
$dReponse['info']="Veuillez avertir l'administrateur de l'application";
}
// wysyłamy odpowiedź
finSession($dConfig,$dReponse,$dSession);
?>
4.15.4. Wyświetlanie widoku końcowego
Oba skrypty generujące dwa widoki końcowe kończą się wywołaniem funkcji kontrolera finSession:
<?php
…
function finSession(&$dConfig,&$dReponse,&$dSession){
// $dConfig: słownik konfiguracyjny
// $dSession: słownik zawierający informacje o sesji
// $dReponse: słownik argumentów strony odpowiedzi
....
// wyświetlanie odpowiedzi
include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];
// koniec skryptu
exit(0);
}//koniec sesji
Widok wysyłany do użytkownika jest definiowany przez encję $dReponse['vuereponse'], która określa szablon, jaki ma zostać użyty w ostatecznej odpowiedzi. W przypadku raportów [e-formulaire] i [e-erreurs] ten szablon został zdefiniowany jako modele1:
Szablon ten odpowiada skryptowi m-reponse.php:
4.16. Modyfikacja szablonu odpowiedzi
Zakładamy tutaj, że zdecydowano się zmienić wygląd odpowiedzi wysyłanej do klienta i chcemy zrozumieć, jakie konsekwencje ma to dla kodu.
4.16.1. Nowy szablon
Struktura odpowiedzi będzie teraz wyglądać następująco:
![]() |
Ten szablon będzie nosił nazwę modele2, a skrypt odpowiedzialny za jego generowanie będzie nosił nazwę m-reponse2.php:
Skrypt odpowiadający temu szablonowi wygląda następująco:
<html>
<head>
<title>Application impots</title>
<link type="text/css" href="<?php echo $dReponse['urlstyle'] ?>" rel="stylesheet" />
</head>
<body>
<table class='modele2'>
<!-- początek banera -->
<tr>
<td colspan="2"><?php include $dReponse['vue1']; ?></td>
</tr>
<!-- koniec banera -->
<tr>
<!-- początek menu -->
<td><?php include $dReponse['vue2']; ?></td>
<!-- koniec menu -->
<!-- początek strefy 3 -->
<td><?php include $dReponse['vue3']; ?></td>
<!-- koniec strefy 3 -->
</tr>
</table>
</body>
</html>
Dynamiczne elementy szablonu są następujące:
- $dReponse['urlstyle']: arkusz stylów, którego należy użyć
- $dReponse['vue1']: skrypt, którego należy użyć do wygenerowania [vue1]
- $dReponse['vue2']: skrypt, którego należy użyć do wygenerowania pliku [vue2]
- $dReponse['vue3']: skrypt służący do wygenerowania pliku [vue3]
Elementy te muszą zostać ustalone przez osoby odpowiedzialne za generowanie widoków.
4.16.2. Różne strony odpowiedzi
Aplikacja będzie teraz wyświetlać użytkownikowi następujące odpowiedzi. Przy pierwszym wywołaniu strona odpowiedzi będzie wyglądać następująco:
![]() |
Jeśli użytkownik poda prawidłowe dane, podatek zostanie obliczony:
![]() |
Jeśli popełni błędy podczas wprowadzania danych, wyświetli się strona błędów:
![]() |
Jeśli skorzysta z linku [Retour au formulaire de saisie], powróci do formularza w stanie, w jakim go zatwierdził:
![]() |
Jeśli w powyższym przypadku skorzysta z linku [Réinitialiser le formulaire], wyświetli się pusty formularz:
![]() |
Należy zauważyć, że aplikacja wykorzystuje te same akcje, co poprzednio. Zmienił się jedynie wygląd odpowiedzi.
4.16.3. Widoki elementarne
Widok podstawowy [vue1] będzie, podobnie jak w poprzednim przykładzie, powiązany ze skryptem 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>
Ten widok zawiera dwa elementy dynamiczne:
- $dReponse['titre']: tytuł do wyświetlenia
- $dReponse['resultat']: kwota podatku do zapłaty
Widok elementarny [vue2] zostanie powiązany z następującym skryptem 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>
Ten widok zawiera następujące elementy dynamiczne:
- $dReponse['liens']: tablica linków do wyświetlenia w [vue2]. Każdy element tablicy jest słownikiem zawierającym dwa klucze:
- „url”: docelowy adres URL linku
- 'tekst': tekst linku
Widok podstawowy [vue3] zostanie powiązany ze skryptem v-formulaire2.php, jeśli chcemy wyświetlić formularz wprowadzania danych, lub ze skryptem v-erreurs2.php, jeśli chcemy wyświetlić stronę błędów. Kod skryptu v-formulaire2.php jest następujący:
<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>
Dynamiczne elementy tego widoku, które muszą zostać zdefiniowane przez generator widoku, są powiązane z następującymi kluczami słownika $dReponse:
- optoui: stan przycisku opcji o nazwie optoui
- optnon: stan przycisku opcji o nazwie optnon
- dzieci: liczba dzieci, którą należy umieścić w polu txtenfants
- salaire: roczne wynagrodzenie, które ma zostać umieszczone w polu txtsalaire
Skrypt generujący stronę błędów nosi nazwę v-erreurs2.php. Jego kod jest następujący:
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";
}//dla
?>
</ul>
<div class="info"><?php echo $dReponse["info"] ?></div>
Dynamiczne elementy tego widoku, które należy zdefiniować za pomocą generatora widoków, są powiązane z następującymi kluczami słownika $dReponse:
- błędy: tablica komunikatów o błędach
- info: komunikat informacyjny
4.16.4. Arkusz stylów
Nie uległ zmianie. Nadal jest to style1.css.
4.16.5. Nowy plik konfiguracyjny
Aby wprowadzić te nowe widoki, musimy zmodyfikować niektóre wiersze w pliku konfiguracyjnym.
<?php
// konfigurację PHP
ini_set("register_globals","off");
ini_set("display_errors","off");
ini_set("expose_php","off");
// lista modułów do dołączenia
$dConfig['includes']=array('c-impots-data.php','c-impots-calcul.php');
// kontroler aplikacji
$dConfig['webapp']=array('titre'=>"Calculez votre impôt");
// konfiguracja widoków aplikacji
$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';
// konfiguracja akcji aplikacji
$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');
// konfiguracja stanów aplikacji
$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'));
// konfiguracja modelu aplikacji
$dConfig["DSN"]=array(
"sgbd"=>"mysql",
"user"=>"seldbimpots",
"mdp"=>"mdpseldbimpots",
"host"=>"localhost",
"database"=>"dbimpots"
);
?>
Najważniejsza zmiana polega na zastąpieniu generatorów widoków powiązanych z raportami [e-formulaire] i [e-erreurs]. Po dokonaniu tej zmiany nowe generatory widoków będą odpowiedzialne za generowanie nowych stron odpowiedzi.
4.16.6. Generator widoku powiązany z raportem [e-formulaire]
W pliku konfiguracyjnym raport [e-formulaire] jest teraz powiązany z generatorem widoku e-formulaire2.php. Kod tego skryptu jest następujący:
<?php
// przygotowywanie odpowiedzi formularza
$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')
);
// konfiguracja w zależności od typu generowanego formularza
$type=$dSession['etat']['secondaire'];
if($type=='init'){
// pusty formularz
$dReponse['optnon']='checked';
}//if
if($type=='calculimpot'){
// należy ponownie wyświetlić parametry wprowadzania danych zapisane w zapytaniu
$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'){
// należy ponownie wyświetlić parametry wprowadzania danych zapisane w sesji
$dReponse['optoui']=$dSession['requete']['optmarie']=='oui' ? 'checked' : '';
$dReponse['optnon']=$dReponse['optoui']=='' ? 'checked' : '';
$dReponse['enfants']=$dSession['requete']['txtenfants'];
$dReponse['salaire']=$dSession['requete']['txtsalaire'];
}//if
// wysyłamy odpowiedź
finSession($dConfig,$dReponse,$dSession);
?>
Główne zmiany są następujące:
- generator widoku wskazuje, że chce użyć szablonu odpowiedzi modele2
- z tego powodu wypełnia elementy dynamiczne $dReponse['vue1'], $dReponse['vue2'], $dReponse['vue3'] – wszystkie trzy niezbędne dla szablonu odpowiedzi modele2.
- generator wypełnia również element dynamiczny $dReponse['liens'], który określa linki do wyświetlenia w obszarze [vue2] odpowiedzi.
4.16.7. Generator widoku powiązany z raportem [e-erreurs]
W pliku konfiguracyjnym raport [e-erreurs] jest teraz powiązany z generatorem widoku e-erreurs2.php. Kod tego skryptu jest następujący:
<?php
// przygotowujemy odpowiedź na błędy
$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')
);
// dodatkowe informacje
$type=$dSession['etat']['secondaire'];
if($type=='database'){
$dReponse['info']="Veuillez avertir l'administrateur de l'application";
}
// wysyłamy odpowiedź
finSession($dConfig,$dReponse,$dSession);
?>
Wprowadzone zmiany są identyczne z tymi wprowadzonymi w generatorze widoku e-formulaire2.php.
4.17. Wnioski
Na przykładzie udało nam się wykazać zalety naszego generycznego kontrolera. Nie musieliśmy go tworzyć od podstaw. Ogranicziliśmy się do napisania skryptów akcji, generatorów widoków oraz samych widoków aplikacji. Ponadto pokazaliśmy korzyści płynące z oddzielenia akcji od widoków. Dzięki temu mogliśmy zmienić wygląd odpowiedzi bez modyfikowania ani jednej linii kodu w skryptach akcji. Zmodyfikowano jedynie skrypty związane z generowaniem widoków. Aby było to możliwe, skrypt akcji nie może opierać się na żadnych założeniach dotyczących widoku, który wyświetli obliczone przez niego informacje. Musi on jedynie przekazać te informacje do kontrolera, który z kolei przekazuje je do generatora widoku, który nadaje im odpowiednią formę. Jest to zasada bezwzględna: akcja musi być całkowicie oddzielona od widoków.
W niniejszym rozdziale zapoznaliśmy się z filozofią Struts, dobrze znaną programistom Java. Projekt „open source” o nazwie php.mvc umożliwia tworzenie aplikacji internetowych w języku PHP zgodnie z filozofią Struts. Więcej informacji można znaleźć na stronie http://www.phpmvc.net/.













