IN() في PDO: كيفية تمرير مصفوفة بدون ألم وأخطاء

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

«هل يمكنني ربط مصفوفة بشرط IN()؟» — سؤال يقلق كل مطور PHP ثاني

\n\n

هل سبق لك أن كتبت استعلامًا مثل SELECT * FROM users WHERE id IN (?) وحاولت تمرير مصفوفة إليه عبر bindValue()؟ إذا كان الأمر كذلك، فأنت بالتأكيد تعرف شعور الإحباط عندما يعيد PDO صفًا واحدًا فقط بدلاً من جميع الصفوف المطلوبة. أو الأسوأ من ذلك — يرمي خطأ.

\n\n

هذا السؤال على Stack Overflow حصد آلاف المشاهدات، وليس من دون سبب. يبدو الأمر بسيطًا: مرر مصفوفة، احصل على النتيجة. لكن PDO (PHP Data Objects) شيء صارم. لا يستطيع «فرد» المصفوفات إلى قائمة قيم لـ IN() تلقائيًا. وهنا يبدأ الجزء المثير للاهتمام: يبتكر المطورون أفضل الأخطاء التي سنناقشها اليوم. والأهم من ذلك — سنتعلم كيفية تجنبها.

\n\n

في هذه المقالة، لن نقدم حلاً جاهزًا فقط. سنسير في الطريق من الكود الخاطئ إلى الكود الآمن والفعال والجميل. هيا بنا!

\n\n

الخطأ رقم 1: تمرير المصفوفة بسذاجة عبر bindValue

\n\n

أكثر «مشكلة» شيوعًا — محاولة إطعام المصفوفة مباشرة إلى bindValue().

\n\n
// ❌ غير صحيح\n$ids = [1, 2, 3, 5];\n$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN (?)");\n$stmt->bindValue(1, $ids, PDO::PARAM_STR);\n$stmt->execute();\n$users = $stmt->fetchAll();
\n\n

ماذا سيحدث؟ سيحول PHP المصفوفة إلى سلسلة نصية "Array"، وسيتحول الاستعلام إلى WHERE id IN ("Array"). النتيجة — صفر صفوف أو خطأ، حسب برنامج التشغيل. PDO ببساطة لا يفهم أن المصفوفة يجب أن تتحلل إلى عناصر نائبة منفصلة.

\n\n

لماذا يحدث هذا؟ يتوقع PDO قيمة قياسية (سلسلة نصية، رقم، null). المصفوفة هي نوع مركب، وهي غير مناسبة للربط المباشر.

\n\n

كيفية الإصلاح؟

\n

تحتاج إلى إنشاء عناصر نائبة يدويًا لكل عنصر من عناصر المصفوفة. لا سحر، فقط كود.

\n\n
// ✅ صحيح\n$ids = [1, 2, 3, 5];\n$placeholders = implode(',', array_fill(0, count($ids), '?'));\n$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN ($placeholders)");\n$stmt->execute($ids);\n$users = $stmt->fetchAll();
\n\n

هنا ننشئ سلسلة نصية بالشكل ?,?,?,? ونمرر المصفوفة مباشرة إلى execute(). سيقوم PDO بتعويض القيم بالترتيب. بسيط وآمن.

\n\n

الخطأ رقم 2: تجاهل أنواع البيانات

\n\n

لنفترض أنك تستخدم عناصر نائبة مسماة. وهنا يظهر الإغراء لفعل ذلك:

\n\n
// ❌ غير صحيح\n$ids = [1, 2, 3];\n$placeholders = [];\nforeach ($ids as $i => $id) {\n    $placeholders[] = ":id_$i";\n}\n$sql = "SELECT * FROM users WHERE id IN (" . implode(',', $placeholders) . ")";\n$stmt = $pdo->prepare($sql);\nforeach ($ids as $i => $id) {\n    $stmt->bindValue(":id_$i", $id, PDO::PARAM_INT);\n}\n$stmt->execute();
\n\n

يبدو كل شيء صحيحًا. لكن هناك فارق بسيط: إذا كانت المصفوفة فارغة، سيصبح الاستعلام WHERE id IN ()، مما يسبب خطأ نحويًا في SQL. المشكلة الثانية — إذا كانت المصفوفة تحتوي على سلاسل نصية، وحددت PDO::PARAM_INT، فقد يحولها PDO إلى صفر.

\n\n

الحل: تحقق دائمًا من أن المصفوفة ليست فارغة وحدد النوع ديناميكيًا.

\n\n
// ✅ صحيح\n$ids = [1, 2, 3];\nif (empty($ids)) {\n    return []; // أو ارمِ استثناءً\n}\n$placeholders = [];\nforeach ($ids as $i => $id) {\n    $placeholders[] = ":id_$i";\n}\n$sql = "SELECT * FROM users WHERE id IN (" . implode(',', $placeholders) . ")";\n$stmt = $pdo->prepare($sql);\nforeach ($ids as $i => $id) {\n    $type = is_int($id) ? PDO::PARAM_INT : PDO::PARAM_STR;\n    $stmt->bindValue(":id_$i", $id, $type);\n}\n$stmt->execute();
\n\n

الخطأ رقم 3: حقن SQL عبر «التنقية» اليدوية

\n\n

بعض المطورين في حالة يأس يكتبون شيئًا مثل

المدونات

توصيات الكتب