Skip to content

8. Exercício IMPOTS com MySQL

Já escrevemos três versões deste exercício. A última versão utilizava uma classe de cálculo de impostos. Essa classe obtinha, de um arquivo de texto, os dados necessários para esse cálculo. Agora, ela os obterá de um banco de dados. Para isso, escrevemos um primeiro código que transferirá os dados do arquivo de texto para um banco de dados.

O arquivo de texto impots.txt é o seguinte:

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

O banco de dados a ser criado é o seguinte:

O banco de dados se chama [dbimpots] e possui uma única tabela, [impots]. Ele é acessado pelo usuário [root], sem senha.

8.1. Transferência de um arquivo de texto para a tabela MySQL (txt2mysql)


<?php

// transfere o arquivo de texto com os dados necessários para o cálculo dos impostos
// para uma tabela do MySQL
// os dados
$IMPOTS = "impots.txt";
$BASE = "dbimpots";
$TABLE = "impots";
$USER = "root";
$PWD = "";
$HOTE = "localhost";

// os dados necessários para o cálculo do imposto foram inseridos no arquivo $IMPOTS
// com uma linha por tabela, no formato
// val1:val2:val3,...
list($erreur, $limites, $coeffR, $coeffN) = getTables($IMPOTS);
// houve algum erro?
if ($erreur) {
  print "$erreur\n";
  exit;
}//if
// esses tabuletos são transferidos para uma tabela do MySQL
$erreur = copyToMysql($limites, $coeffR, $coeffN, $HOTE, $USER, $PWD, $BASE, $TABLE);
if ($erreur)
  print "$erreur\n";
else
  print "Transfert opéré\n";
// fim
exit;

// --------------------------------------------------------------------------
function copyToMysql($limites, $coeffR, $coeffN, $HOTE, $USER, $PWD, $BASE, $TABLE) {
  // copia as três tabelas numéricas $limites, $coeffR, $coeffN
  // na tabela $TABLE do banco de dados MySQL $BASE
  // o banco de dados MySQL está na máquina $HOTE
  // o usuário é identificado por $USER e $PWD
  // conexão
  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";
  // exclusão da tabela
  $requête = "drop table $TABLE";
  exécuteRequête($connexion, $requête);
  // criação da tabela
  $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)";
  // preenchimento
  for ($i = 0; $i < count($limites); $i++) {
    // consulta de inserção
    $requête = "insert into $TABLE (limites,coeffR,coeffN) values ($limites[$i],$coeffR[$i],$coeffN[$i])";
    // execução da consulta
    list($erreur, $res) = exécuteRequête($connexion, $requête);
    // retorno em caso de erro
    if ($erreur)
      return "$requête : erreur ($erreur)";
  }//for
  // concluído — encerrando a sessão
  déconnecte($connexion);
  // retorno sem erro
  return "";
}
// --------------------------------------------------------------------------
function getTables($IMPOTS) {
  // $IMPOTS: o nome do arquivo que contém os dados das tabelas $limites, $coeffR, $coeffN
  ...
  // fim
  return array("", $limites, $coeffR, $coeffN);
}
// --------------------------------------------------------------------------
function cutNewLinechar($ligne) {
  // elimina-se o marcador de fim de linha de $ligne, caso exista
  ...
  // fim
  return($ligne);
}
// ---------------------------------------------------------------------------------
function connecte($dsn, $login, $pwd) {
  // conecta ($login,$pwd) ao banco de dados $dsn
  // retorna o ID da conexão e uma mensagem de erro
  ...
}

//estabelece conexão
// ---------------------------------------------------------------------------------
function déconnecte($connexion) {
  // encerra a conexão identificada por $connexion
  ...
}

// ---------------------------------------------------------------------------------
function exécuteRequête($connexion, $sql) {
  // executa a consulta $sql na conexão $connexion
  // retorna uma matriz com 2 elementos ($erreur, $résultat)
 ...
}

Resultados na tela:

Transfert opéré

8.2. O programa de cálculo de impostos ( impots_04)

Esta versão utiliza uma classe Impôts que busca em um banco de dados os valores necessários para o cálculo do imposto. Introduzimos aqui um novo conceito: o de consulta preparada. A execução de uma ordem SQL por meio de uma SGBD ocorre em duas etapas:

  1. a consulta é preparada: o SGBD prepara um plano de execução otimizado para a consulta. O objetivo é executá-la da forma mais eficiente possível.
  1. a consulta é executada.

Se uma mesma consulta for executada N vezes, as duas etapas anteriores são realizadas N vezes. No entanto, é possível preparar a consulta uma vez e executá-la N vezes. Para isso, é necessário utilizar consultas preparadas. Se $requête for a ordem SQL a ser executada e $connexion for o objeto PDO que representa a conexão:

  • $statement=$connexion->prepare($requête) prepara uma consulta e retorna a consulta “preparada”
  • $statement->execute() executa a consulta preparada.

Se a consulta preparada for uma ordem select, então

  • $statement->fetchAll() retorna todas as linhas da tabela resultante do select na forma de uma matriz T, em que T[i,j] é o valor da coluna j da linha i da tabela
  • $statement->fetch() retorna a linha atual da tabela na forma de um array T, em que T[j] é o valor da coluna j da linha

A consulta preparada traz outras vantagens além de uma maior eficiência. Ela proporciona, principalmente, mais segurança. Portanto, deve ser utilizada sistematicamente.


<?php

// teste -----------------------------------------------------
// definição das constantes
$DATA = "data.txt";
$RESULTATS = "resultats.txt";
$TABLE = "impots";
$BASE = "dbimpots";
$USER = "root";
$PWD = "";
$HOTE="localhost";

// os dados necessários para o cálculo do imposto foram inseridos na tabela mysqL $TABLE
// pertencente à base de dados $BASE. A tabela possui a seguinte estrutura
// limites decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
// parâmetros dos contribuintes (estado civil, número de filhos, salário anual)
// foram inseridos no arquivo de texto $DATA, com uma linha por contribuinte
// os resultados (estado civil, número de filhos, salário anual, imposto a pagar) são inseridos no
// no arquivo de texto $RESULTATS, com um resultado por linha

// é criado um objeto Impostos
$I = new Impôts($HOTE, $USER, $PWD, $BASE, $TABLE);
// houve algum erro?
$erreur=$I->getErreur();
if ($erreur) {
  print "$I->erreur\n";
  exit;
}//if
// é criado um objeto de utilitários
$u = new Utilitaires();

// abertura do arquivo de dados dos contribuintes
$data = fopen($DATA, "r");
if (!$data) {
  print "Impossible d'ouvrir en lecture le fichier des données [$DATA]\n";
  exit;
}

// abertura do arquivo de resultados
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
  print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
  exit;
}

// processa-se a linha atual do arquivo de dados dos contribuintes
while ($ligne = fgets($data, 100)) {
  // remove-se a eventual marca de fim de linha
  $ligne = $u->cutNewLineChar($ligne);
  // recupera-se os três campos “casado”, “filhos” e “salário” que formam $ligne
  list($marié, $enfants, $salaire) = explode(",", $ligne);
  // calcula-se o imposto
  $impôt = $I->calculer($marié, $enfants, $salaire);
  // insere-se o resultado
  fputs($résultats, "$marié:$enfants:$salaire:$impôt\n");
  // próximo dado
}
// fecha-se os arquivos
fclose($data);
fclose($résultats);

// fim
exit;

// ---------------------------------------------------------------------------------
// definição de uma classe Impostos
class Impôts {

  // atributos: as 3 tabelas de dados
  private $limites;
  private $coeffR;
  private $coeffN;
  private $erreur;
  private $nbLimites;

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

  function __construct($HOTE, $USER, $PWD, $BASE, $TABLE) {
    // inicializa os atributos $limites, $coeffR, $coeffN
    // os dados necessários para o cálculo do imposto foram inseridos na tabela MySQL $TABLE
    //, pertencente ao banco de dados $BASE. A tabela possui a seguinte estrutura
    // limites decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
    // a conexão com o banco de dados MySQL da máquina $HOTE é feita com a identidade ($USER,$PWD)
    // inicializa o campo $erreur com um possível erro
    // fica vazio se não houver erro
    // 
    // conexão com o banco de dados 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;
    }
    // leitura da tabela $TABLE
    $requête = "select limites,coeffR,coeffN from $TABLE";
    // executa a consulta $requête na conexão $connexion
    try {
      $statement = $connexion->prepare($requête);
      $statement->execute();
      // análise do resultado da consulta
      while ($colonnes = $statement->fetch()) {
        $this->limites[] = $colonnes[0];
        $this->coeffR[] = $colonnes[1];
        $this->coeffN[] = $colonnes[2];
      }
      // sem erros
      $this->erreur = "";
      // número de elementos da matriz de limites
      $this->nbLimites = count($this->limites);
    } catch (PDOException $e) {
      $this->erreur = $e->getMessage();
    }
     // desconexão
    déconnecte($connexion);
  }
  // --------------------------------------------------------------------------
  function calculer($marié, $enfants, $salaire) {
    // $marié: sim, não
    // $enfants: número de filhos
    // $salaire: salário anual
    
  // o objeto está em bom estado?
    if ($this->erreur)
      return -1;

    // número de cotas
    ...
  }

}

// ---------------------------------------------------------------------------------
// uma classe de funções utilitárias
class Utilitaires {

  function cutNewLinechar($ligne) {
    // remove-se o marcador de fim de linha de $ligne, caso exista
    ...
  }

}
// ---------------------------------------------------------------------------------
function connecte($dsn, $login, $pwd) {
  // conecta ($login, $pwd) ao banco de dados $dsn
  // retorna o ID da conexão e uma mensagem de erro
  ...
}

//estabelece conexão
// ---------------------------------------------------------------------------------
function déconnecte($connexion) {
  // encerra a conexão identificada por $connexion
  ...
}

Resultados: os mesmos das versões anteriores.

Comentários

As novidades estão nas linhas 98-109:

  • linha 99: a ordem SQL select, que permitirá recuperar os dados necessários para o cálculo do imposto.
  • linha 102: preparação da ordem SQL select
  • linha 103: execução da ordem preparada
  • linhas 105-109: análise linha por linha da tabela de resultados do select