Skip to content

8. تمرين IMPOTS مع MySQL

لقد كتبنا بالفعل ثلاث نسخ من هذا التمرين. استخدمت النسخة الأخيرة فئة لحساب الضريبة. كانت هذه الفئة تستمد البيانات اللازمة لهذا الحساب من ملف نصي. وستستمدها الآن من قاعدة بيانات. ولهذا الغرض، نكتب كودًا أوليًّا لنقل البيانات من الملف النصي إلى قاعدة البيانات.

الملف النصي impots.txt هو كما يلي:

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

قاعدة البيانات المطلوب إنشاؤها هي كما يلي:

تسمى قاعدة البيانات [dbimpots] وتحتوي على جدول واحد [impots]. يتم تشغيلها بواسطة المستخدم [root] بدون كلمة مرور.

8.1. نقل ملف نصي إلى الجدول MySQL (txt2mysql)


<?php

// نقل ملف نصي للبيانات اللازمة لحساب الضرائب
// إلى جدول MySQL
// البيانات
$IMPOTS = "impots.txt";
$BASE = "dbimpots";
$TABLE = "impots";
$USER = "root";
$PWD = "";
$HOTE = "localhost";

// تم وضع البيانات اللازمة لحساب الضريبة في الملف $IMPOTS
// بمعدل سطر واحد لكل جدول بالشكل
// val1:val2:val3,...
list($erreur, $limites, $coeffR, $coeffN) = getTables($IMPOTS);
// هل حدث خطأ؟
if ($erreur) {
  print "$erreur\n";
  exit;
}//if
// ننقل هذه الجداول إلى جدول mysql
$erreur = copyToMysql($limites, $coeffR, $coeffN, $HOTE, $USER, $PWD, $BASE, $TABLE);
if ($erreur)
  print "$erreur\n";
else
  print "Transfert opéré\n";
// نهاية
exit;

// --------------------------------------------------------------------------
function copyToMysql($limites, $coeffR, $coeffN, $HOTE, $USER, $PWD, $BASE, $TABLE) {
  // نسخ الجداول الرقمية الثلاثة $limites، $coeffR، $coeffN
  // إلى الجدول $TABLE في قاعدة بيانات mysql $BASE
  // قاعدة بيانات mysql موجودة على الجهاز $HOTE
  // يتم تعريف المستخدم بواسطة $USER و $PWD
  // تسجيل الدخول
  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";
  // حذف الجدول
  $requête = "drop table $TABLE";
  exécuteRequête($connexion, $requête);
  // إنشاء الجدول
  $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)";
  // التعبئة
  for ($i = 0; $i < count($limites); $i++) {
    // استعلام الإدراج
    $requête = "insert into $TABLE (limites,coeffR,coeffN) values ($limites[$i],$coeffR[$i],$coeffN[$i])";
    // تنفيذ الاستعلام
    list($erreur, $res) = exécuteRequête($connexion, $requête);
    // العودة في حالة وجود خطأ
    if ($erreur)
      return "$requête : erreur ($erreur)";
  }//for
  // انتهى الأمر - يتم قطع الاتصال
  déconnecte($connexion);
  // العودة دون أخطاء
  return "";
}
// --------------------------------------------------------------------------
function getTables($IMPOTS) {
  // $IMPOTS: اسم الملف الذي يحتوي على بيانات الجداول $limites، $coeffR، $coeffN
  ...
  // نهاية
  return array("", $limites, $coeffR, $coeffN);
}
// --------------------------------------------------------------------------
function cutNewLinechar($ligne) {
  // يتم حذف علامة نهاية السطر من $ligne إن وجدت
  ...
  // نهاية
  return($ligne);
}
// ---------------------------------------------------------------------------------
function connecte($dsn, $login, $pwd) {
  // يُربط ($login,$pwd) بالقاعدة $dsn
  // يعرض معرف الاتصال بالإضافة إلى رسالة خطأ
  ...
}

//يقوم بالاتصال
// ---------------------------------------------------------------------------------
function déconnecte($connexion) {
  // يغلق الاتصال المحدد بـ $connexion
  ...
}

// ---------------------------------------------------------------------------------
function exécuteRequête($connexion, $sql) {
  // يُنفذ الاستعلام $sql على الاتصال $connexion
  // تُرجع مصفوفة مكونة من عنصرين ($erreur، $résultat)
 ...
}

نتائج الشاشة:

Transfert opéré

8.2. برنامج حساب الضريبة ( impots_04)

تستخدم هذه النسخة فئة Impôts التي تبحث في قاعدة البيانات عن القيم اللازمة لحساب الضريبة. نقدم هنا مفهومًا جديدًا، وهو مفهوم الاستعلام المُعد مسبقًا. يتم تنفيذ الأمر SQL بواسطة SGBD على مرحلتين:

  1. يتم إعداد الاستعلام: يقوم الأمر SGBD بإعداد خطة تنفيذ مُحسَّنة للاستعلام. والهدف من ذلك هو تنفيذه بأكبر قدر ممكن من الكفاءة.
  1. يتم تنفيذ الطلب.

إذا تم تنفيذ نفس الاستعلام N مرات، يتم تنفيذ المرحلتين السابقتين N مرات. ومع ذلك، من الممكن إعداد الاستعلام مرة واحدة وتنفيذه N مرات. ولتحقيق ذلك، يجب استخدام الاستعلامات المُعدة مسبقًا. إذا كان $requête هو الأمر SQL المطلوب تنفيذه، و$connexion هو الكائن PDO الذي يمثل الاتصال:

  • $statement=$connexion->prepare($requête) تقوم بإعداد استعلام وتجعل الاستعلام "مُعدًا"
  • $statement->execute() تنفذ الاستعلام المُعد.

إذا كان الاستعلام المُعدّ هو الأمر select، فإن

  • $statement->fetchAll() تُرجع جميع صفوف الجدول الناتج عن select في شكل مصفوفة T حيث T[i,j] هي قيمة العمود j في الصف i من الجدول
  • $statement->fetch() تُرجع السطر الحالي من الجدول في شكل مصفوفة T، حيث T[j] هي قيمة العمود j في السطر

توفر الاستعلامات المعدة مسبقًا مزايا أخرى بخلاف الكفاءة العالية. فهي توفر على وجه الخصوص مزيدًا من الأمان. لذا ينبغي استخدامها بشكل منهجي.


<?php

// اختبار -----------------------------------------------------
// تعريف الثوابت
$DATA = "data.txt";
$RESULTATS = "resultats.txt";
$TABLE = "impots";
$BASE = "dbimpots";
$USER = "root";
$PWD = "";
$HOTE="localhost";

// تم وضع البيانات اللازمة لحساب الضريبة في الجدول mysqL $TABLE
// التابعة لقاعدة البيانات $BASE. الجدول له البنية التالية
// حدود decimal(10,2)، coeffR decimal(6,2)، coeffN decimal(10,2)
// معلمات الأشخاص الخاضعين للضريبة (الحالة الاجتماعية، عدد الأبناء، الراتب السنوي)
// تم وضعها في الملف النصي $DATA بواقع سطر واحد لكل دافع ضرائب
// تم وضع النتائج (الحالة الاجتماعية، عدد الأطفال، الراتب السنوي، الضريبة المستحقة) في
// الملف النصي $RESULTATS بحيث يتم إدراج نتيجة واحدة في كل سطر

// يتم إنشاء كائن «الضرائب»
$I = new Impôts($HOTE, $USER, $PWD, $BASE, $TABLE);
// هل حدث خطأ؟
$erreur=$I->getErreur();
if ($erreur) {
  print "$I->erreur\n";
  exit;
}//if
// يتم إنشاء كائن أدوات مساعدة
$u = new Utilitaires();

// فتح ملف بيانات دافعي الضرائب
$data = fopen($DATA, "r");
if (!$data) {
  print "Impossible d'ouvrir en lecture le fichier des données [$DATA]\n";
  exit;
}

// فتح ملف النتائج
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
  print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
  exit;
}

// يتم معالجة السطر الحالي من ملف بيانات دافعي الضرائب
while ($ligne = fgets($data, 100)) {
  // إزالة علامة نهاية السطر إن وجدت
  $ligne = $u->cutNewLineChar($ligne);
  // استرجاع الحقول الثلاثة «متزوج» و«أطفال» و«الراتب» التي تشكل $ligne
  list($marié, $enfants, $salaire) = explode(",", $ligne);
  // يتم حساب الضريبة
  $impôt = $I->calculer($marié, $enfants, $salaire);
  // يتم تدوين النتيجة
  fputs($résultats, "$marié:$enfants:$salaire:$impôt\n");
  // البيانات التالية
}
// يتم إغلاق الملفات
fclose($data);
fclose($résultats);

// النهاية
exit;

// ---------------------------------------------------------------------------------
// تعريف فئة الضرائب
class Impôts {

  // السمات: جداول البيانات الثلاثة
  private $limites;
  private $coeffR;
  private $coeffN;
  private $erreur;
  private $nbLimites;

  // مُستخرج
  public function getErreur(){
    return $this->erreur;
  }
  // منشئ

  function __construct($HOTE, $USER, $PWD, $BASE, $TABLE) {
    // تقوم بتهيئة الخصائص $limites، $coeffR، $coeffN
    // تم وضع البيانات اللازمة لحساب الضريبة في جدول mysql $TABLE
    // التابعة لقاعدة البيانات $BASE. الجدول له البنية التالية
    // حدود decimal(10,2)، coeffR decimal(6,2)، coeffN decimal(10,2)
    // يتم الاتصال بقاعدة بيانات mysql الخاصة بالجهاز $HOTE باستخدام الهوية ($USER,$PWD)
    // يقوم بتهيئة الحقل $erreur مع وجود خطأ محتمل
    // يُفرغ الحقل في حالة عدم وجود خطأ
    // 
    // الاتصال بقاعدة بيانات 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;
    }
    // قراءة الجدول $TABLE
    $requête = "select limites,coeffR,coeffN from $TABLE";
    // تنفيذ الاستعلام $requête على الاتصال $connexion
    try {
      $statement = $connexion->prepare($requête);
      $statement->execute();
      // استخدام نتيجة الاستعلام
      while ($colonnes = $statement->fetch()) {
        $this->limites[] = $colonnes[0];
        $this->coeffR[] = $colonnes[1];
        $this->coeffN[] = $colonnes[2];
      }
      // لا توجد أخطاء
      $this->erreur = "";
      // عدد عناصر المصفوفة في الحدود
      $this->nbLimites = count($this->limites);
    } catch (PDOException $e) {
      $this->erreur = $e->getMessage();
    }
     // قطع الاتصال
    déconnecte($connexion);
  }
  // --------------------------------------------------------------------------
  function calculer($marié, $enfants, $salaire) {
    // $marié: نعم، لا
    // $enfants: عدد الأطفال
    // $salaire: الراتب السنوي
    
  // هل الحالة سليمة؟
    if ($this->erreur)
      return -1;

    // عدد الحصص
    ...
  }

}

// ---------------------------------------------------------------------------------
// فئة من الدوال المساعدة
class Utilitaires {

  function cutNewLinechar($ligne) {
    // يتم حذف علامة نهاية السطر من $ligne إن وجدت
    ...
  }

}
// ---------------------------------------------------------------------------------
function connecte($dsn, $login, $pwd) {
  // ربط ($login، $pwd) بقاعدة البيانات $dsn
  // يعرض معرف الاتصال بالإضافة إلى رسالة خطأ
  ...
}

//يقوم بالاتصال
// ---------------------------------------------------------------------------------
function déconnecte($connexion) {
  // يغلق الاتصال المحدد بـ $connexion
  ...
}

النتائج: نفس النتائج التي تم الحصول عليها مع الإصدارات السابقة.

تعليقات

المستجدات موجودة في الأسطر 98-109:

  • السطر 99: الأمر SQL select الذي سيسمح باسترداد البيانات اللازمة لحساب الضريبة.
  • السطر 102: إعداد الأمر SQL select
  • السطر 103: تنفيذ الأمر المُعد
  • الأسطر 105-109: معالجة كل سطر على حدة من جدول نتائج select