PHP / متوسط: الكائنات والحفظ والاختبار / الدرس 10
PDO وSQL المحضرة والمعاملات
اربط البيانات منفصلة عن بنية SQL وافحص الثوابت. تنشئ كل التجارب SQLite في الذاكرة ولا تقرأ اتصالات التطبيق المضبوطة.
في هذه الصفحة
أهداف الدرس
- ربط القيم وحصر خيارات بنية SQL.
- استخدام عقود خطأ وجلب صريحة.
- فحص التراجع وتمييز سلوك المحرك.
PDO واجهة بسلوك يعتمد على المشغل
توفر PDO واجهة مشتركة لكن تختلف SQL والمعاملات والأنواع والتحضير بالمشغل. افحص الامتداد وحدد نمط الخطأ والجلب. أبعد الاعتمادات وDSN التطبيق عن المثال والخطأ العام. تنشئ sqlite::memory: قاعدة معزولة للاتصال، لا اختبار MySQL أو إنتاج. يثبت النجاح التفاعل والثابت محلياً لا تزامن أو مقارنة محرك آخر.
المعاملات قيم لا معرفات SQL
حضّر بمواضع واربط القيم عبر execute أو الربط. يفصل ذلك البيانات عن التحليل في المسار المدعوم. لا تمثل المواضع عموماً الجدول والعمود والاتجاه وقطع SQL؛ اخترها من سماح مملوك. لا تقتبس القيمة المربوطة يدوياً ولا تفترض تكرار الاسم متطابقاً بكل مشغل. يستخدم المثال اسماً واحداً وبنية ثابتة، وتحفظ فاصلة الاقتباس كبيانات.
<?php
declare(strict_types=1);
$db = new PDO("sqlite::memory:", null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$db->exec("CREATE TABLE courses (id INTEGER PRIMARY KEY, title TEXT NOT NULL)");
$insert = $db->prepare("INSERT INTO courses (title) VALUES (:title)");
$insert->execute(["title" => "Ada's PHP"]);
$read = $db->prepare("SELECT title FROM courses WHERE id = :id");
$read->execute(["id" => 1]);
echo $read->fetchColumn(), PHP_EOL;
Ada's PHP
تبقى العلامة بالعنوان ولا تبنى SQL منه ولا اتصال خارجي.
تحمي المعاملة مجموعة كتابات مترابطة
حدّد ما يجب اعتماده معاً عند تغيير سجلات. ابدأ واكتب واعتمد للنجاح وتراجع للفشل إذا بقيت المعاملة نشطة. لا تفترض catch بدء كل معاملة أو ذرية DDL بكل محرك. استخدم القيود للتفرد والمفاتيح؛ فقد يتسابق فحص التطبيق. افحص الحالة بعد نقطة فشل مضبوطة لا استدعاء rollback فقط.
إدخال الإخفاق بعد بدء العمل
يجهز المثال رصيد عشرة ويغيره داخل معاملة ثم يرمي قبل الاعتماد، فيبقى عشرة بعد التراجع. لا يثبت رمي قبل الكتابة تراجع العمل الجزئي. لا تنافس في الاتصال الواحد؛ فتحتاج الحجوزات محركاً وعزلاً مقصودين. وضح المالك كيلا يعتمد مساعد ما يملكه المستدعي. ولا تفترض أن التراجع يعكس فعلاً خارجياً غير قابل للإلغاء.
<?php
declare(strict_types=1);
$db = new PDO("sqlite::memory:", null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$db->exec("CREATE TABLE balance (amount INTEGER NOT NULL)");
$db->exec("INSERT INTO balance VALUES (10)");
try {
$db->beginTransaction();
$db->exec("UPDATE balance SET amount = 3");
throw new RuntimeException("controlled failure");
} catch (RuntimeException $error) {
if ($db->inTransaction()) { $db->rollBack(); }
}
echo $db->query("SELECT amount FROM balance")->fetchColumn(), PHP_EOL;
10
حدث التحديث قبل الفشل ولم يعتمد، والدليل لهذه المعاملة المعزولة فقط.
بقاء الشكل والترميز والكلفة صريحة
اختر شكل الجلب وميّز عدم الصف من null داخله. تحقق من الانتقال للأنواع ولا تفترض تماثل المشغلات. يمنع الربط فئة حقن ولا يفرض الإذن أو التهريب أو كفاءة الخطة. حدّ النتائج ولا تجلب الكل لصفحة. تتبع ترميز الاتصال والبايتات بالمحرك المقصود عند الفساد ولا تكرر التحويل للتجميل. وثّق افتراضات المحرك والمتوقع.
الأخطاء الشائعة وإصلاحها
استخدام موضع لعمود ORDER BY
- لماذا يحدث؟
- الخلط بين القيمة وبنية SQL.
- كيف تصلحه؟
- اختيار عمود بسماح ثابت وربط القيم فقط.
- كيف تتجنب تكراره؟
- مراجعة البنية الديناميكية مستقلة عن البيانات.
ادعاء التراجع دون فحص الحالة
- لماذا يحدث؟
- رصد استدعاء لا الأثر المحفوظ.
- كيف تصلحه؟
- الفشل بعد كتابة وفحص الثابت النهائي.
- كيف تتجنب تكراره؟
- توثيق نقطة الفشل ومالك المعاملة.
تعميم نجاح SQLite لمحرك آخر
- لماذا يحدث؟
- إغفال اختلاف المشغل والعزل.
- كيف تصلحه؟
- إضافة فحوص محرك مقصود للمخاطر.
- كيف تتجنب تكراره؟
- وسم دليل المحرك وحدوده.
اختبر فهمك
1. يختار المستخدم الترتيب. هل تربط اسم العمود كـ:column؟
اعرض الحل المشروح
ليس كاستبدال معرف عام. اربط الاختيار بسماح عمود واتجاه مملوك وابنِ البنية المحدودة واربط القيم مستقلة. ارفض المجهول أو استخدم افتراضاً موثقاً لا SQL خاماً.
2. ما الذي يثبت تراجع تحديث جزئي؟
اعرض الحل المشروح
جهز حالة واكتب داخل المعاملة وأدخل الفشل قبل الاعتماد واقرأ بعد التراجع. توقع الأصل بلا صفوف جزئية. الفشل قبل الكتابة أو رصد الاستدعاء أضعف.
أهم ما تعلمته
- اربط البيانات واحصر البنية وافحص الثوابت النهائية.
- لا تلغي واجهة PDO اختلاف المحركات.