8. Ćwiczenie IMPOTS z MySQL
Napisaliśmy już trzy wersje tego ćwiczenia. W ostatniej wersji wykorzystano klasę służącą do obliczania podatku. Klasa ta pobierała dane niezbędne do obliczeń z pliku tekstowego. Od teraz będzie je pobierać z bazy danych. W tym celu piszemy pierwszy kod, który przeniesie dane z pliku tekstowego do bazy danych.
Plik tekstowy impots.txt ma następującą treść:
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
Baza danych, którą należy utworzyć, wygląda następująco:
![]() | ![]() |
Baza nosi nazwę [dbimpots] i zawiera jedną tabelę o nazwie [impots]. Korzysta z niej użytkownik [root] bez hasła.
8.1. Przeniesienie pliku tekstowego do tabeli MySQL (txt2mysql)
<?php
// przesyła plik tekstowy zawierający dane niezbędne do obliczenia podatków
// do tabeli MySQL
// dane
$IMPOTS = "impots.txt";
$BASE = "dbimpots";
$TABLE = "impots";
$USER = "root";
$PWD = "";
$HOTE = "localhost";
// dane niezbędne do obliczenia podatku zostały umieszczone w pliku $IMPOTS
// w postaci jednego wiersza na tabelę w formacie
// val1:val2:val3,...
list($erreur, $limites, $coeffR, $coeffN) = getTables($IMPOTS);
// czy wystąpił błąd?
if ($erreur) {
print "$erreur\n";
exit;
}//if
// przenosimy te tabele do tabeli MySQL
$erreur = copyToMysql($limites, $coeffR, $coeffN, $HOTE, $USER, $PWD, $BASE, $TABLE);
if ($erreur)
print "$erreur\n";
else
print "Transfert opéré\n";
// koniec
exit;
// --------------------------------------------------------------------------
function copyToMysql($limites, $coeffR, $coeffN, $HOTE, $USER, $PWD, $BASE, $TABLE) {
// kopiuje 3 tabele liczbowe $limites, $coeffR, $coeffN
// do tabeli $TABLE w bazie danych MySQL $BASE
// baza danych MySQL znajduje się na komputerze $HOTE
// użytkownik jest identyfikowany przez $USER i $PWD
// logowanie
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";
// usunięcie tabeli
$requête = "drop table $TABLE";
exécuteRequête($connexion, $requête);
// utworzenie tabeli
$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)";
// wypełnianie
for ($i = 0; $i < count($limites); $i++) {
// zapytanie o wstawienie
$requête = "insert into $TABLE (limites,coeffR,coeffN) values ($limites[$i],$coeffR[$i],$coeffN[$i])";
// wykonanie zapytania
list($erreur, $res) = exécuteRequête($connexion, $requête);
// powrót w przypadku błędu
if ($erreur)
return "$requête : erreur ($erreur)";
}//for
// koniec – rozłączamy się
déconnecte($connexion);
// powrót bez błędu
return "";
}
// --------------------------------------------------------------------------
function getTables($IMPOTS) {
// $IMPOTS: nazwa pliku zawierającego dane z tabel $limites, $coeffR, $coeffN
...
// koniec
return array("", $limites, $coeffR, $coeffN);
}
// --------------------------------------------------------------------------
function cutNewLinechar($ligne) {
// usuwa się znacznik końca linii z pliku $ligne, jeśli istnieje
...
// koniec
return($ligne);
}
// ---------------------------------------------------------------------------------
function connecte($dsn, $login, $pwd) {
// łączy ($login,$pwd) z bazą $dsn
// zwraca identyfikator połączenia oraz komunikat o błędzie
...
}
//nawiązuje połączenie
// ---------------------------------------------------------------------------------
function déconnecte($connexion) {
// zamyka połączenie o identyfikatorze $connexion
...
}
// ---------------------------------------------------------------------------------
function exécuteRequête($connexion, $sql) {
// wykonuje zapytanie $sql na połączeniu $connexion
// zwraca tablicę zawierającą 2 elementy ($erreur, $résultat)
...
}
Wyniki wyświetlane na ekranie:
8.2. Program do obliczania podatku ( impots_04)
W tej wersji wykorzystywana jest klasa Impôts, która pobiera z bazy danych wartości niezbędne do obliczenia podatku. W tym miejscu wprowadzamy nowe pojęcie – zapytanie przygotowane. Wykonanie zlecenia SQL przez SGBD odbywa się w dwóch etapach:
- przygotowanie zapytania: program SGBD przygotowuje zoptymalizowany plan wykonania zapytania. Chodzi o to, aby wykonać je w możliwie najbardziej efektywny sposób.
- zapytań jest wykonywane.
Jeśli to samo zapytanie zostanie wykonane N razy, powyższe dwa etapy są powtarzane N razy. Możliwe jest jednak przygotowanie zapytania tylko raz, a następnie wykonanie go N razy. W tym celu należy korzystać z zapytań przygotowanych. Jeśli $requête jest poleceniem SQL do wykonania, a $connexion jest obiektem PDO reprezentującym połączenie:
- $statement=$connexion->prepare($requête) przygotowuje zapytanie i zwraca zapytanie jako „przygotowane”
- $statement->execute() wykonuje przygotowane zapytanie.
Jeśli przygotowanym zapytaniem jest polecenie select, to
- $statement->fetchAll() zwraca wszystkie wiersze tabeli wynikowej select w postaci tablicy T, gdzie T[i,j] jest wartością kolumny j w wierszu i tabeli
- $statement->fetch() zwraca bieżący wiersz tabeli w postaci tablicy T, gdzie T[j] jest wartością kolumny j w wierszu
Zapytanie przygotowane zapewnia nie tylko większą wydajność, ale także inne korzyści. Zapewnia ono w szczególności większe bezpieczeństwo. Dlatego należy je stosować systematycznie.
<?php
// test -----------------------------------------------------
// definicja stałych
$DATA = "data.txt";
$RESULTATS = "resultats.txt";
$TABLE = "impots";
$BASE = "dbimpots";
$USER = "root";
$PWD = "";
$HOTE="localhost";
// dane niezbędne do obliczenia podatku zostały umieszczone w tabeli mysqL $TABLE
// należącej do bazy danych $BASE. Tabela ma następującą strukturę
// limity decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
// parametry podatników (stan cywilny, liczba dzieci, roczne wynagrodzenie)
// zostały umieszczone w pliku tekstowym $DATA, po jednym wierszu na podatnika
// wyniki (stan cywilny, liczba dzieci, roczne wynagrodzenie, podatek do zapłaty) zostały umieszczone w
// plik tekstowy $RESULTATS, po jednym wyniku w każdym wierszu
// tworzy się obiekt „Podatki”
$I = new Impôts($HOTE, $USER, $PWD, $BASE, $TABLE);
// czy wystąpił błąd?
$erreur=$I->getErreur();
if ($erreur) {
print "$I->erreur\n";
exit;
}//if
// tworzymy obiekt narzędziowy
$u = new Utilitaires();
// otwieranie pliku z danymi podatników
$data = fopen($DATA, "r");
if (!$data) {
print "Impossible d'ouvrir en lecture le fichier des données [$DATA]\n";
exit;
}
// otwieranie pliku wyników
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
exit;
}
// przetwarzamy bieżący wiersz pliku danych podatników
while ($ligne = fgets($data, 100)) {
// usuwamy ewentualny znacznik końca wiersza
$ligne = $u->cutNewLineChar($ligne);
// pobieranie trzech pól: „żonaty”, „dzieci” i „wynagrodzenie”, które tworzą $ligne
list($marié, $enfants, $salaire) = explode(",", $ligne);
// oblicza się podatek
$impôt = $I->calculer($marié, $enfants, $salaire);
// wpisuje się wynik
fputs($résultats, "$marié:$enfants:$salaire:$impôt\n");
// kolejny rekord
}
// zamykamy pliki
fclose($data);
fclose($résultats);
// koniec
exit;
// ---------------------------------------------------------------------------------
// definicja klasy „Podatki”
class Impôts {
// atrybuty: 3 tabele danych
private $limites;
private $coeffR;
private $coeffN;
private $erreur;
private $nbLimites;
// metoda pobierająca
public function getErreur(){
return $this->erreur;
}
// konstruktor
function __construct($HOTE, $USER, $PWD, $BASE, $TABLE) {
// inicjuje atrybuty $limites, $coeffR, $coeffN
// dane niezbędne do obliczenia podatku zostały umieszczone w tabeli MySQL o nazwie $TABLE
// należącej do bazy danych $BASE. Tabela ma następującą strukturę
// limity decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
// połączenie z bazą danych MySQL na serwerze $HOTE odbywa się przy użyciu identyfikatora ($USER,$PWD)
// inicjuje pole $erreur, uwzględniając ewentualny błąd
// jest puste, jeśli nie wystąpił błąd
//
// połączenie z bazą danych 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;
}
// odczytuje tabelę $TABLE
$requête = "select limites,coeffR,coeffN from $TABLE";
// wykonuje zapytanie $requête na połączeniu $connexion
try {
$statement = $connexion->prepare($requête);
$statement->execute();
// przetwarzanie wyniku zapytania
while ($colonnes = $statement->fetch()) {
$this->limites[] = $colonnes[0];
$this->coeffR[] = $colonnes[1];
$this->coeffN[] = $colonnes[2];
}
// brak błędu
$this->erreur = "";
// liczba elementów tablicy limitów
$this->nbLimites = count($this->limites);
} catch (PDOException $e) {
$this->erreur = $e->getMessage();
}
// rozłączenie
déconnecte($connexion);
}
// --------------------------------------------------------------------------
function calculer($marié, $enfants, $salaire) {
// $marié: tak, nie
// $enfants: liczba dzieci
// $salaire: roczne wynagrodzenie
// czy przedmiot jest w dobrym stanie?
if ($this->erreur)
return -1;
// liczba udziałów
...
}
}
// ---------------------------------------------------------------------------------
// klasa funkcji pomocniczych
class Utilitaires {
function cutNewLinechar($ligne) {
// usuwa się znak końca linii z $ligne, jeśli istnieje
...
}
}
// ---------------------------------------------------------------------------------
function connecte($dsn, $login, $pwd) {
// łączy ($login,$pwd) z bazą $dsn
// zwraca identyfikator połączenia oraz komunikat o błędzie
...
}
//nawiązuje połączenie
// ---------------------------------------------------------------------------------
function déconnecte($connexion) {
// zamyka połączenie o identyfikatorze $connexion
...
}
Wyniki: takie same jak w przypadku poprzednich wersji.
Komentarze
Nowości znajdują się w wierszach 98–109:
- wiersz 99: polecenie SQL select, które pozwoli na pobranie danych niezbędnych do obliczenia podatku.
- wiersz 102: przygotowanie zlecenia SQL select
- wiersz 103: wykonanie przygotowanego zlecenia
- wiersze 105–109: przetwarzanie wiersz po wierszu tabeli wynikowej z select

