طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 301 · من الفكرة إلى المنتج (١): تطبيق ويب متكامل
الدرس 3 · الوحدة 2: بناء المنتج

المحطّة ٣: إنشاء القاعدة وطبقة الوصول (PDO)

نفّذ مخطّطك: قاعدةٌ تعمل، وطبقة PDO تقرأ منها بأمان. ⏱ نحو 24 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تنشئ قاعدة بياناتٍ من مخطّطك وتصل بها عبر PDO.
  • تضع الاتصال في طبقةٍ واحدةٍ db.php قابلةٍ للتبديل بين SQLite و MySQL.
  • تقرأ الصفوف بـ query و fetchAll وتعرضها.
  • تربط الجدولين بـ JOIN وتمرّر القيَم بأمانٍ عبر المعاملات المُعدّة.

١من التصميم إلى قاعدةٍ تعمل

معنا الآن مخطّطٌ من المحطّة السابقة. نحوّله إلى قاعدةٍ حقيقيّة ونصل بها من PHP. نضع الاتصال في ملفٍّ واحدٍ db.php نضمّنه في كل صفحةٍ تحتاج القاعدة — فلو تغيّر الاتصال (من SQLite في المختبر إلى MySQL على XAMPP) عدّلنا مكانًا واحدًا. هذا ما أتقنته في مقرّر 132، ونبني عليه هنا.

<?php// db.php: الاتصال بقاعدة البيانات في مكانٍ واحد (نضمّنه في كل صفحة)$pdo = new PDO('sqlite:' . __DIR__ . '/tasks.sqlite');// على XAMPP بدّل السطر أعلاه بهذا:// $pdo = new PDO('mysql:host=localhost;dbname=tasks;charset=utf8mb4', 'root', ''); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);        // الأخطاء استثناءات$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);   // الصفوف مصفوفات ترابطية

الإعدادان سطرٌ واحدٌ لكنهما يوفّران ساعاتٍ من التصحيح: الأوّل يحوّل أخطاء القاعدة إلى استثناءاتٍ تراها فورًا، والثاني يجعل كل صفٍّ مصفوفةً مفاتيحها أسماء الأعمدة.

٢أول قراءة: كل المهامّ

في المختبر القاعدة مهيّأةٌ ببيانات تجربة (مستخدمان ومهامّهما). نرسل استعلامًا بسيطًا ونعرض الصفوف. جرّب تشغيله:

<?phprequire __DIR__ . '/db.php'; $rows = $pdo->query('SELECT id, title, done FROM tasks ORDER BY id')->fetchAll(); echo "<p>عدد المهامّ: " . count($rows) . "</p><ul>";foreach ($rows as $t) {    $mark = $t['done'] ? '✓' : '•';    echo "<li>{$mark} {$t['title']}</li>";}echo "</ul>";

لاحظ: query() تصلح للاستعلامات الثابتة التي لا تحوي مدخلاتٍ من المستخدم. حين يدخل المستخدم قيمةً (بحثٌ، معرّف…) ننتقل إلى الاستعلامات المُعدّة (prepared) لمنع الحقن — وهذا موضوع محطّة التأمين.

٣عدّ المهامّ المنجَزة لمستخدمٍ محدّد

مثالٌ على ربط الجدولين: نحسب كم مهمّةً أنجزها المستخدم «sarah». نمرّر اسم المستخدم كـ معامل مُعدّ (آمن) ونربط الجدولين:

<?phprequire __DIR__ . '/db.php'; $st = $pdo->prepare(  'SELECT COUNT(*) AS c     FROM tasks t JOIN users u ON u.id = t.user_id    WHERE u.username = ? AND t.done = 1');$st->execute(['sarah']);$n = $st->fetch()['c']; echo "أنجزت sarah {$n} مهمّة.";

توقّع وتحقّق

لماذا نضع الاتصال في ملفٍّ واحدٍ db.php؟

ما الذي يحوّل أخطاء القاعدة إلى استثناءاتٍ تراها فورًا؟

أيّهما يصلح لقيمةٍ أدخلها المستخدم؟

المُعدّة تمنع الحقن.

ماذا يفعل JOIN في استعلام الإحصاء؟

الخلاصة

تحدٍّ برمجي: صفحة «كل المهامّ»

برمجي

اكتب صفحة tasks.php تتّصل بالقاعدة عبر db.php، ثم تعرض عدد المهامّ الكلّيّ وقائمةً بعناوينها كلّها.

المطلوب:
  • ضمّن db.php.
  • اقرأ كل المهامّ مرتّبةً بالمعرّف.
  • اطبع سطرًا: عدد المهامّ: 5.
  • اطبع قائمة <ul> فيها عنوان كل مهمّة.
👀 اعرض الاستجابة المطلوبة
✓ التحقق الآلي: يرسل المختبر هذه الطلبات إلى خادمك بالترتيب ثم يفحص الاستجابات
  1. GET /tasks.php
  • الكود يعمل بلا أخطاء أو تحذيرات
  • يعرض العدد الصحيح (5)
  • يعرض عناوين المهامّ
  • يستخدم قائمة ul

💡 تلميح 1
اقرأ الصفوف بـ $pdo->query(...)->fetchAll().
💡 تلميح 2
العدد بـ count($rows)، والعناوين بحلقة foreach.

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

<?phprequire __DIR__ . '/db.php'; $rows = $pdo->query('SELECT title FROM tasks ORDER BY id')->fetchAll(); echo "عدد المهامّ: " . count($rows);echo "<ul>";foreach ($rows as $t) {    echo "<li>{$t['title']}</li>";}echo "</ul>";

🐞 لاحظت خطأً في هذا الدرس؟ أبلغني بالبريد

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس