Skip to content

8. Esercizio IMPOTS con MySQL

Abbiamo già scritto tre versioni di questo esercizio. L'ultima versione utilizzava una classe per il calcolo delle imposte. Questa classe prelevava da un file di testo i dati necessari per il calcolo. D'ora in poi li preleverà da un database. A tal fine, scriviamo un primo codice che trasferirà i dati dal file di testo a un database.

Il file di testo impots.txt è il seguente:

12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:272.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062

Il database da creare è il seguente:

Il database si chiama [dbimpots] e contiene un'unica tabella, [impots]. Viene gestito dall'utente [root] senza password.

8.1. Trasferimento di un file di testo in una tabella MySQL (txt2mysql)


<?php

// trasferisce il file di testo contenente i dati necessari al calcolo delle imposte
// in una tabella MySQL
// i dati
$IMPOTS = "impots.txt";
$BASE = "dbimpots";
$TABLE = "impots";
$USER = "root";
$PWD = "";
$HOTE = "localhost";

// i dati necessari per il calcolo dell'imposta sono stati inseriti nel file $IMPOTS
// con una riga per ogni tabella nel formato
// val1:val2:val3,...
list($erreur, $limites, $coeffR, $coeffN) = getTables($IMPOTS);
// Si è verificato un errore?
if ($erreur) {
  print "$erreur\n";
  exit;
}//if
// si trasferiscono queste tabelle in un database MySQL
$erreur = copyToMysql($limites, $coeffR, $coeffN, $HOTE, $USER, $PWD, $BASE, $TABLE);
if ($erreur)
  print "$erreur\n";
else
  print "Transfert opéré\n";
// fine
exit;

// --------------------------------------------------------------------------
function copyToMysql($limites, $coeffR, $coeffN, $HOTE, $USER, $PWD, $BASE, $TABLE) {
  // copia le 3 tabelle numeriche $limites, $coeffR, $coeffN
  // nella tabella $TABLE del database MySQL $BASE
  // il database MySQL si trova sul computer $HOTE
  // l'utente è identificato da $USER e $PWD
  // connessione
  list($erreur, $connexion) = connecte("mysql:host=$HOTE;dbname=$BASE", $USER, $PWD);
  if ($erreur)
    return "Erreur lors de la connexion à MySql sous l'identité ($HOTE,$USER,$PWD) : $erreur\n";
  // eliminazione della tabella
  $requête = "drop table $TABLE";
  exécuteRequête($connexion, $requête);
  // creazione della tabella
  $requête = "create table $TABLE (limites decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2))";
  list($erreur, $res) = exécuteRequête($connexion, $requête);
  if ($erreur)
    return "$requête : erreur ($erreur)";
  // compilazione
  for ($i = 0; $i < count($limites); $i++) {
    // query di inserimento
    $requête = "insert into $TABLE (limites,coeffR,coeffN) values ($limites[$i],$coeffR[$i],$coeffN[$i])";
    // esecuzione della query
    list($erreur, $res) = exécuteRequête($connexion, $requête);
    // ritorno in caso di errore
    if ($erreur)
      return "$requête : erreur ($erreur)";
  }//ciclo
  // finito - ci disconnettiamo
  déconnecte($connexion);
  // ritorno senza errori
  return "";
}
// --------------------------------------------------------------------------
function getTables($IMPOTS) {
  // $IMPOTS: il nome del file contenente i dati delle tabelle $limites, $coeffR, $coeffN
  ...
  // fine
  return array("", $limites, $coeffR, $coeffN);
}
// --------------------------------------------------------------------------
function cutNewLinechar($ligne) {
  // si elimina il carattere di fine riga da $ligne, se presente
  ...
  // fine
  return($ligne);
}
// ---------------------------------------------------------------------------------
function connecte($dsn, $login, $pwd) {
  // connette ($login,$pwd) al database $dsn
  // restituisce l'ID della connessione e un messaggio di errore
  ...
}

//si connette
// ---------------------------------------------------------------------------------
function déconnecte($connexion) {
  // chiude la connessione identificata da $connexion
  ...
}

// ---------------------------------------------------------------------------------
function exécuteRequête($connexion, $sql) {
  // esegue la query $sql sulla connessione $connexion
  // restituisce un array di 2 elementi ($erreur,$résultat)
 ...
}

Risultati sullo schermo:

Transfert opéré

8.2. Il programma di calcolo delle imposte ( impots_04)

Questa versione utilizza una classe Impôts che recupera da un database i valori necessari per il calcolo dell'imposta. Introduciamo qui un nuovo concetto, quello di query preparata. L’esecuzione di un comando SQL tramite un SGBD avviene in due fasi:

  1. la query viene preparata: il SGBD preparerà un piano di esecuzione ottimizzato per la query. L'obiettivo è eseguirla nel modo più efficiente possibile.
  1. la query viene eseguita.

Se una stessa query viene eseguita N volte, le due fasi precedenti vengono ripetute N volte. È tuttavia possibile preparare la query una sola volta ed eseguirla N volte. A tal fine è necessario utilizzare le query preparate. Se $requête è il comando SQL da eseguire e $connexion è l’oggetto PDO che rappresenta la connessione:

  • $statement=$connexion->prepare($requête) prepara una query e restituisce la query "preparata"
  • $statement->execute() esegue la query preparata.

Se la query preparata è un comando select, allora

  • $statement->fetchAll() restituisce tutte le righe della tabella risultante da select sotto forma di un array T, dove T[i,j] è il valore della colonna j della riga i della tabella
  • $statement->fetch() restituisce la riga corrente della tabella sotto forma di un array T, dove T[j] è il valore della colonna j della riga

La query preparata offre altri vantaggi oltre a una maggiore efficienza. In particolare, garantisce una maggiore sicurezza. Pertanto, dovrebbe essere utilizzata sistematicamente.


<?php

// test -----------------------------------------------------
// definizione delle costanti
$DATA = "data.txt";
$RESULTATS = "resultats.txt";
$TABLE = "impots";
$BASE = "dbimpots";
$USER = "root";
$PWD = "";
$HOTE="localhost";

// i dati necessari per il calcolo dell'imposta sono stati inseriti nella tabella mysqL $TABLE
// appartenente al database $BASE. La tabella presenta la seguente struttura
// limiti decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
// i parametri dei contribuenti (stato civile, numero di figli, stipendio annuo)
// sono stati inseriti nel file di testo $DATA con una riga per ogni contribuente
// i risultati (stato civile, numero di figli, stipendio annuo, imposta da pagare) sono stati inseriti nel
// nel file di testo $RESULTATS, con un risultato per riga

// viene creato un oggetto «Imposte»
$I = new Impôts($HOTE, $USER, $PWD, $BASE, $TABLE);
// si è verificato un errore?
$erreur=$I->getErreur();
if ($erreur) {
  print "$I->erreur\n";
  exit;
}//if
// si crea un oggetto "utilità"
$u = new Utilitaires();

// apertura del file dei dati dei contribuenti
$data = fopen($DATA, "r");
if (!$data) {
  print "Impossible d'ouvrir en lecture le fichier des données [$DATA]\n";
  exit;
}

// apertura del file dei risultati
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
  print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
  exit;
}

// si elabora la riga corrente del file dei dati dei contribuenti
while ($ligne = fgets($data, 100)) {
  // si rimuove l'eventuale carattere di fine riga
  $ligne = $u->cutNewLineChar($ligne);
  // si recuperano i 3 campi «coniuge», «figli» e «stipendio» che formano $ligne
  list($marié, $enfants, $salaire) = explode(",", $ligne);
  // si calcola l’imposta
  $impôt = $I->calculer($marié, $enfants, $salaire);
  // si inserisce il risultato
  fputs($résultats, "$marié:$enfants:$salaire:$impôt\n");
  // dato successivo
}
// si chiudono i file
fclose($data);
fclose($résultats);

// fine
exit;

// ---------------------------------------------------------------------------------
// definizione di una classe «Imposte»
class Impôts {

  // attributi: le 3 tabelle di dati
  private $limites;
  private $coeffR;
  private $coeffN;
  private $erreur;
  private $nbLimites;

  // getter
  public function getErreur(){
    return $this->erreur;
  }
  // costruttore

  function __construct($HOTE, $USER, $PWD, $BASE, $TABLE) {
    // inizializza gli attributi $limites, $coeffR, $coeffN
    // i dati necessari per il calcolo dell'imposta sono stati inseriti nella tabella MySQL $TABLE
    // appartenente al database $BASE. La tabella presenta la seguente struttura
    // limiti decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
    // la connessione al database MySQL del computer $HOTE avviene con le credenziali ($USER,$PWD)
    // inizializza il campo $erreur con un eventuale errore
    // vuoto se non ci sono errori
    // 
    // connessione al database MySQL
    $DSN = "mysql:host=$HOTE;dbname=$BASE";
    list($erreur, $connexion) = connecte($DSN, $USER, $PWD);
    if ($erreur) {
      $this->erreur = "Erreur lors de la connexion à MySql sous l'identité ($HOTE,$USER,$PWD) : $erreur\n";
      return;
    }
    // lettura della tabella $TABLE
    $requête = "select limites,coeffR,coeffN from $TABLE";
    // esegue la query $requête sulla connessione $connexion
    try {
      $statement = $connexion->prepare($requête);
      $statement->execute();
      // elaborazione del risultato della query
      while ($colonnes = $statement->fetch()) {
        $this->limites[] = $colonnes[0];
        $this->coeffR[] = $colonnes[1];
        $this->coeffN[] = $colonnes[2];
      }
      // nessun errore
      $this->erreur = "";
      // numero di elementi dell'array di limiti
      $this->nbLimites = count($this->limites);
    } catch (PDOException $e) {
      $this->erreur = $e->getMessage();
    }
     // disconnessione
    déconnecte($connexion);
  }
  // --------------------------------------------------------------------------
  function calculer($marié, $enfants, $salaire) {
    // $marié: sì, no
    // $enfants: numero di figli
    // $salaire: stipendio annuo
    
  // L'oggetto è in buone condizioni?
    if ($this->erreur)
      return -1;

    // numero di quote
    ...
  }

}

// ---------------------------------------------------------------------------------
// una classe di funzioni di utilità
class Utilitaires {

  function cutNewLinechar($ligne) {
    // si rimuove il carattere di fine riga da $ligne, se presente
    ...
  }

}
// ---------------------------------------------------------------------------------
function connecte($dsn, $login, $pwd) {
  // collega ($login,$pwd) al database $dsn
  // restituisce l'ID della connessione e un messaggio di errore
  ...
}

//si connette
// ---------------------------------------------------------------------------------
function déconnecte($connexion) {
  // chiude la connessione identificata da $connexion
  ...
}

Risultati: gli stessi delle versioni precedenti.

Commenti

Le novità si trovano nelle righe 98-109:

  • riga 99: il comando SQL select che consentirà di recuperare i dati necessari al calcolo dell'imposta.
  • riga 102: preparazione del comando SQL select
  • riga 103: esecuzione del comando preparato
  • righe 105-109: elaborazione riga per riga della tabella dei risultati di select