انتقل إلى محتوى الدرس
الدرس 83
دورة PHP

الفلترة بشرط WHERE والترتيب بـ ORDER BY وتقسيم الصفحات Pagination في PDOPDO Filtering with WHERE Sorting and Pagination

ابنِ فلاتر بحث ديناميكية بـ LIKE وIN، وأمّن الترتيب بـ ORDER BY، ونفّذ تقسيم الصفحات Pagination عبر LIMIT وOFFSET في PDO.
أقسام الدرس
6
الأمثلة
6
اللغة
العربية
تاريخ النشر
غير محدد
المشاهدات
0
محتوى الدرس
في هذا الدرس 6تصفّح المواضيع

صفِّ سجلات قاعدة البيانات بدمج شروط WHERE المنطقية والرقمية مع المعاملات المحضرة المربوطة بأمان.

  • يجب تمثيل كل قيمة ديناميكية داخل شرط WHERE بمعامل محضر (:status و:minPrice) بدلاً من الدمج النصي المباشر.
  • عند الجمع بين AND وOR في استعلام SQL، أحِط مجموعة OR بقوسين لضمان وضوح أولوية التنفيذ.
  • استدعِ fetchAll(PDO::FETCH_ASSOC) بعد $stmt->execute() لجلب كافة الصفوف المطابقة كمصفوفة ترابطية.

يُربط المعاملان :lvl و:max بأمان داخل execute() لإرجاع الدورات التي تحقق الشرطين معاً.

مخطط توضيحي: محاكي الفلترة بشرط WHERE والترتيب الآمن وتقسيم الصفحات في PDO
PHP
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE courses (title TEXT, level TEXT, price INT)');
$pdo->exec("INSERT INTO courses VALUES ('PHP Core', 'beginner', 0), ('PDO Pro', 'advanced', 90)");
$stmt = $pdo->prepare('SELECT title FROM courses WHERE level = :lvl AND price <= :max');
$stmt->execute(['lvl' => 'beginner', 'max' => 50]);
$rows = $stmt->fetchAll(PDO::FETCH_COLUMN);
echo "Matched: " . implode(', ', $rows) . "\n";

عند استخدام البحث الجزئي LIKE مع الاستعلامات المحضرة، أضف علامات النسبة المئوية % داخل قيمة المتغير في PHP، ولا تضع علامات تنصيص حول المعامل داخل جملة SQL.

  • اكتب في جملة الاستعلام WHERE title LIKE :search، ومرّر القيمة في PHP على الصورة ["search" => "%$keyword%"].
  • كتابة LIKE "%:search%" داخل نص SQL خطأ شائع؛ لأن PDO يعتبرها نصاً حرفياً ثابتاً ولن يربط المعامل :search.
  • لمنع المستخدم من إدخال رموز البحث % أو _ كحروف عادية، يمكنك تحييدها مسبقاً عبر addcslashes($keyword, "%_").

تستخدم جملة SQL المعامل المجرد LIKE :q، بينما يضيف كود PHP علامتي النسبة المئوية في القيمة الممررة "%$term%".

PHP
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE articles (id INTEGER PRIMARY KEY, title TEXT)');
$pdo->exec("INSERT INTO articles (title) VALUES ('Mastering PHP Streams'), ('Intro to CSS Grid')");
$term = 'PHP';
$stmt = $pdo->prepare('SELECT title FROM articles WHERE title LIKE :q');
$stmt->execute(['q' => "%$term%"]);
echo "Found: " . $stmt->fetchColumn() . "\n";

لا يمكن لمعامل محضر واحد (WHERE id IN (:ids)) أن يربط مصفوفة كاملة؛ بل يجب توليد سلسلة من علامات ? تطابق عدد عناصر المصفوفة ديناميكياً.

  • استخدم التعبير implode(", ", array_fill(0, count($ids), "?")) لبناء نص المعاملات الموضعية بأمان مثل "?, ?, ?".
  • افحص أولاً أن مصفوفة $ids ليست فارغة، لأن كتابة WHERE id IN () فارغة تسبب خطأ نحوياً في SQL.
  • مرّر المصفوفة المفهرسة array_values($ids) مباشرة إلى $stmt->execute() لربط كل علامة ? بقيمتها بالترتيب.

تولّد array_fill(0, 3, "?") ثلاث علامات استفهام ليقوم الشرط IN (?, ?, ?) بربط المعرفات الثلاثة بأمان.

PHP
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE items (id INT PRIMARY KEY, name TEXT)');
$pdo->exec("INSERT INTO items VALUES (1, 'Pen'), (2, 'Book'), (3, 'Bag'), (4, 'Lamp')");
$ids = [1, 3, 4];
$in = implode(', ', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("SELECT name FROM items WHERE id IN ($in)");
$stmt->execute(array_values($ids));
echo implode(', ', $stmt->fetchAll(PDO::FETCH_COLUMN)) . "\n";

لا تربط المعاملات المحضرة إلا قيم البيانات فقط، ولا يمكنها ربط أسماء الأعمدة أو اتجاه الترتيب (ASC وDESC) في جملة ORDER BY.

  • إذا كتبت ORDER BY :sort، فستعتبر قاعدة البيانات المعامل نصاً ثابتاً ولن تقوم بترتيب الصفوف فعلياً.
  • تحقق دائماً من عمود الترتيب المطلوب عبر قائمة بيضاء صارمة بالدالة in_array($sort, $allowedColumns, true) مع تحديد عمود افتراضي آمن.
  • اضبط اتجاه الترتيب بصرامة عبر تعبير مثل $dir = strtoupper($inputDir) === "DESC" ? "DESC" : "ASC";.

لا يُدرج اسم العمود في ORDER BY إلا بعد تأكيد وجوده ضمن القائمة البيضاء المعتمدة عبر in_array($userSort, $allowed, true).

مخطط تدفق التنفيذ العملي لقسم حماية أعمدة الترتيب ORDER BY بالقائمة البيضاء في لغة PHP
PHP
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE products (id INT, price INT)');
$pdo->exec('INSERT INTO products VALUES (1, 300), (2, 120), (3, 450)');
$userSort = 'price';
$allowed = ['id', 'price'];
$col = in_array($userSort, $allowed, true) ? $userSort : 'id';
$dir = 'ASC';
$prices = $pdo->query("SELECT price FROM products ORDER BY $col $dir")->fetchAll(PDO::FETCH_COLUMN);
echo implode(' -> ', $prices) . "\n";

قسّم نتائج الجداول الكبيرة إلى صفحات باستخدام LIMIT :limit OFFSET :offset مع ربط القيمتين كأعداد صحيحة عبر PDO::PARAM_INT.

  • احسب إزاحة البداية من رقم الصفحة الحالي بالمعادلة: $offset = ($page - 1) * $perPage مع ضمان أن الصفحة لا تقل عن 1.
  • اربط المعاملين :limit و:offset عبر $stmt->bindValue(":limit", $perPage, PDO::PARAM_INT) حتى لا يحيطهما PDO بعلامات تنصيص.
  • اربط دائماً تقسيم الصفحات بترتيب حاسم مثل ORDER BY id DESC لضمان عدم تداخل أو تكرار السجلات عند الانتقال بين الصفحات.

في الصفحة الثانية بعنصرين لكل صفحة، تكون الإزاحة (2 - 1) * 2 = 2، فيتخطى الاستعلام أول سجلين ويُرجع P3, P4.

PHP
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE posts (id INT PRIMARY KEY, title TEXT)');
$pdo->exec("INSERT INTO posts VALUES (1,'P1'),(2,'P2'),(3,'P3'),(4,'P4'),(5,'P5')");
$page = 2;
$perPage = 2;
$offset = ($page - 1) * $perPage;
$stmt = $pdo->prepare('SELECT title FROM posts ORDER BY id ASC LIMIT :lim OFFSET :off');
$stmt->bindValue(':lim', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':off', $offset, PDO::PARAM_INT);
$stmt->execute();
echo "Page 2: " . implode(', ', $stmt->fetchAll(PDO::FETCH_COLUMN)) . "\n";

اجمع بين ربط المعاملات للقيم، والقوائم البيضاء لأسماء الأعمدة، والربط الرقمي لتقسيم الصفحات لضمان سرعة وأمان كافة استعلامات PDO.

  • استخدم استعلام COUNT(*) بنفس شروط WHERE لحساب إجمالي عدد الصفحات عبر $totalPages = (int) ceil($totalRows / $perPage).
  • تذكر أن تمرير مصفوفة إلى $stmt->execute([...]) يربط جميع عناصرها كنصوص PDO::PARAM_STR افتراضياً؛ لذا استخدم bindValue(..., PDO::PARAM_INT) مع أرقام التقسيم الصفحي.
  • لا تثق أبداً بمعاملات الرابط مثل $_GET["sort"] أو $_GET["page"] دون فحص وتطهير.

مع وجود 45 سجلاً و10 سجلات لكل صفحة، تعطي ceil(45 / 10) إجمالي 5 صفحات، وتبدأ الصفحة الثالثة عند الإزاحة 20.

جزء استعلام SQLالطريقة الآمنة للتعامل مع المدخلاتالخطأ الشائع الواجب تجنبه
WHERE col = :valBind via execute(["val" => $v])Interpolating "$v" directly into SQL
WHERE col LIKE :qBind ["q" => "%$term%"] in PHPWriting LIKE "%:q%" inside SQL string
WHERE id IN (?, ?, ?)Generate ? via array_fill()Binding an array to a single IN (:ids)
ORDER BY / LIMITWhitelist column; bindValue(PDO::PARAM_INT)Binding column name as ORDER BY :col

PHP
<?php
$totalRows = 45;
$perPage = 10;
$page = max(1, 3);
$totalPages = (int) ceil($totalRows / $perPage);
$offset = ($page - 1) * $perPage;
echo "Page $page of $totalPages | LIMIT $perPage OFFSET $offset\n";
خلاصة المعرفة ومحطة الانتقال

ملخص الدرس

حصاد الدرس

مراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي

ماذا تعلّمت في هذا الدرس؟

اجمع بين ربط المعاملات للقيم، والقوائم البيضاء لأسماء الأعمدة، والربط الرقمي لتقسيم الصفحات لضمان سرعة وأمان كافة استعلامات PDO. استخدم استعلام COUNT(*) بنفس شروط WHERE لحساب إجمالي عدد الصفحات عبر $totalPages = (int) ceil($totalRows / $perPage). تذكر أن تمرير مصفوفة إلى $stmt->execute([...]) يربط جميع عناصرها كنصوص PDO::PARAM_STR افتراضياً؛ لذا استخدم bindValue(..., PDO::PARAM_INT) مع أرقام التقسيم الصفحي. لا تثق أبداً بمعاملات الرابط مثل $_GET["sort"] أو $_GET["page"] دون فحص وتطهير.

مشاركة

هل كان هذا الدرس مفيدًا؟

جارٍ تحميل الآراء…

ابنِ لغتك البرمجية

المصطلحات4

راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.

0 / 4 مفهومةأول بطاقة، أول خطوة
01 WHERE, ORDER & LIMIT الفلترة والترتيب و LIMIT
ابنِ فلاتر بحث ديناميكية بـ LIKE وIN، وأمّن الترتيب بـ ORDER BY، ونفّذ تقسيم الصفحات Pagination عبر LIMIT وOFFSET في PDO.
للمراجعة
02 Filtering Rows with WHERE and Parameters فلترة السجلات بشرط WHERE والمعاملات
المفهوم الأساسي لقسم فلترة السجلات بشرط WHERE والمعاملات في لغة PHP الحديثة.
للمراجعة
03 Safe Pattern Matching with LIKE البحث الجزئي الآمن بالمعامل LIKE
الصياغة البرمجية والتطبيق العملي المباشر لقسم البحث الجزئي الآمن بالمعامل LIKE.
للمراجعة
04 Dynamic IN Clauses with Placeholders بناء شرط IN الديناميكي بالمعاملات المحضرة
قواعد التحقق وأفضل الممارسات القياسية الخاصة بقسم بناء شرط IN الديناميكي بالمعاملات المحضرة.
للمراجعة

اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.

حروف قليلة. معنى كامل.

الاختصارات4

راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.

0 / 4 مفهومةأول بطاقة، أول خطوة
01 PHP PHP
PHP: Hypertext Preprocessor
لغة البرمجة النصية على جانب الخادم لبناء تطبيقات الويب الديناميكية وواجهات البرمجة.
للمراجعة
02 CLI CLI
Command-Line Interface
واجهة سطر الأوامر التي تتيح تشغيل ملفات PHP مباشرة عبر الطرفية.
للمراجعة
03 FPM FPM
FastCGI Process Manager
مدير عمليات FastCGI عالي الأداء لمعالجة طلبات خادم الويب في PHP.
للمراجعة
04 HTML HTML
HyperText Markup Language
لغة الترميز القياسية التي يولدها محرك PHP ويرسلها إلى متصفح العميل بعد التنفيذ.
للمراجعة

اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.

من الفهم للتطبيق

مهامك قبل الاختبار

خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.

0/2مهام منجزة
  1. أنشئ ملف PHP محلياً ونفّذ المثال البرمجي المشروح في قسم فلترة السجلات بشرط WHERE والمعاملات للتحقق من المخرجات.

  2. بدّل بين الحالات الثلاث في المحاكي التفاعلي ضمن قسم البحث الجزئي الآمن بالمعامل LIKE وقارن مخرجات التنفيذ.

تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.

مساحة التحدي

اختبر فهمك.
ووصلها للإتقان.

كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.

8أسئلة 1أنواع تحديات 100%هدف الإتقان
تصحيح تلقائي. فرص بلا حدود.

أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.

الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.

لمحة عن التحدي8 أسئلة تنتظرك
01

كيف يجب كتابة استعلام البحث الجزئي LIKE المحضر بطريقة صحيحة وآمنة في PDO؟

توضع علامات النسبة المئوية % داخل قيمة المتغير الممرر في PHP وليس حول اسم المعامل في SQL.

02

لماذا يفشل الترتيب عند محاولة ربط اسم العمود كمعامل محضر مثل ORDER BY :sortColumn؟

تتعامل قاعدة البيانات مع المعامل المربوط كقيمة نصية ثابتة وليس كاسم عمود؛ لذا يجب استخدام القائمة البيضاء.

03

كيف تنفذ استعلام WHERE id IN (...) بأمان لمصفوفة ديناميكية في PHP مثل $ids = [4, 8, 15]؟

يحصل كل عنصر في المصفوفة على معامل موضعي ? مستقل يتم ربطه بأمان.

04

ما المعادلة الصحيحة لحساب إزاحة البداية (OFFSET) للصفحة رقم $page عند عرض $perPage عنصراً في كل صفحة؟

الصفحة 1 تبدأ من الإزاحة 0، والصفحة 2 تبدأ من الإزاحة 10 (عند عرض 10 عناصر).

05

لماذا يُفضّل ربط :limit و:offset عبر $stmt->bindValue(..., PDO::PARAM_INT) بدلاً من تمريرهما في مصفوفة execute([...])؟

يضمن الثابت PDO::PARAM_INT إرسال أرقام صحيحة بدون علامات تنصيص في جملة LIMIT.

06

أي فحص في PHP يتحقق بأمان من أن عمود الترتيب المطلوب $sort ضمن الأعمدة المسموح بها؟

يضمن الفحص الصارم بـ in_array(..., true) عدم تمرير أي نص خارج القائمة البيضاء المعتمدة.

07

لماذا يجب أن يتضمن كل استعلام مقسم لصفحات (LIMIT / OFFSET) جملة ترتيب صريحة وحاسمة ORDER BY؟

لا تضمن قواعد البيانات ثبات ترتيب الصفوف عبر الصفحات إلا بوجود ORDER BY صريح.

08

إذا كان عدد السجلات المطابقة 95 سجلاً وعرض الصفحة $perPage = 10، فكيف تحسب إجمالي عدد الصفحات في PHP؟

تقرب الدالة ceil(9.5) الناتج للأعلى إلى 10 صفحات لعرض آخر 5 سجلات متبقية في الصفحة العاشرة.