في بعض الحالات، نحتاج لجلب البيانات من قاعدة البيانات بترتيب عشوائي مختلف في كل مرة، بدلاً من الترتيب الافتراضي (حسب id أو تاريخ الإنشاء مثلاً). من الأمثلة الشائعة على ذلك: أنظمة الاختبارات الإلكترونية، حيث نريد أن يظهر للطالب سؤال مختلف في كل مرة، مع ظهور خيارات الإجابة بترتيب مختلف أيضًا في كل محاولة، لتقليل فرص الغش أو حفظ الترتيب.
توفّر Laravel لهذا الغرض دالة جاهزة وبسيطة وهي inRandomOrder.
الاستخدام الأساسي: ترتيب عشوائي لجدول واحد
لجلب جميع الأسئلة بترتيب عشوائي في كل مرة يتم فيها تنفيذ الاستعلام:
$questions = Question::inRandomOrder()->get();
في كل مرة يُنفَّذ فيها هذا الكود، سيختلف ترتيب النتائج المُرجعة تلقائيًا، دون الحاجة لأي منطق إضافي أو خلط يدوي للمصفوفة بعد جلب البيانات.
الترتيب العشوائي عبر علاقة (Relationship)
الحالة الأكثر فائدة عمليًا هي عند التعامل مع علاقة بين جدولين، مثل نظام امتحان يحتوي على أسئلة (Questions) وإجابات (Answers) لكل سؤال. الهدف هنا مضاعف: جلب الأسئلة بترتيب عشوائي، وفي نفس الوقت جلب إجابات كل سؤال بترتيب عشوائي أيضًا.
لتحقيق ذلك، نستخدم inRandomOrder داخل تعريف العلاقة نفسها ضمن with:
$questions = Question::with(['answers' => function ($q) {
$q->inRandomOrder();
}])->inRandomOrder()->get();توضيح
- inRandomOrder() الأولى (المطبقة مباشرة على Question) تُرتّب الأسئلة نفسها بشكل عشوائي.
- inRandomOrder() الثانية (داخل Closure الخاصة بـ answers) تُرتّب إجابات كل سؤال بشكل عشوائي مستقل.
- لاحظ أن الـ Closure يجب أن تستقبل المتغير $q كوسيط صريح (function ($q))، وليس بدون وسيط، وإلا سيصبح $q غير معرَّف داخل الدالة ويؤدي ذلك إلى خطأ عند التنفيذ.
بهذا الشكل، نحصل في كل مرة على أسئلة بترتيب مختلف، وكل سؤال منها يظهر بإجابات مرتبة عشوائيًا أيضًا، وهو بالضبط ما يحتاجه نظام امتحانات إلكتروني يريد تقليل إمكانية الحفظ أو الغش بالاعتماد على ترتيب ثابت.
ملاحظة حول الأداء
تعتمد inRandomOrder داخليًا على دالة ترتيب عشوائي في قاعدة البيانات (مثل ORDER BY RAND() في MySQL). هذه الطريقة تعمل بشكل ممتاز مع الجداول الصغيرة والمتوسطة، لكنها قد تصبح أبطأ نسبيًا مع الجداول الكبيرة جدًا (بعشرات أو مئات الآلاف من السجلات)، لأن قاعدة البيانات تحتاج لحساب قيمة عشوائية لكل صف قبل الترتيب. في حال كان الجدول ضخمًا وظهر تأثير ملحوظ على الأداء، يمكن اللجوء لحلول بديلة مثل الترتيب العشوائي على مستوى التطبيق بعد تحديد نطاق (LIMIT) أصغر من البيانات.
شكرا لك تعديل بسيط يجب ان يصبح الكود بهذا الشكل $questions = Question::with([\'answers\' => function($q) { $q->inRandomOrder(); }])->inRandomOrder()->get();
شكرًا جزيلًا لك على هذا التنبيه، وأنت محق تمامًا. الكود الأصلي كان ناقصًا لأن الـ Closure لم يستقبل $q كوسيط:
function () { $q->inRandomOrder(); // $q غير معرّف هنا }وهذا كان سيؤدي إلى خطأ Undefined variable $q عند التنفيذ، لأن المتغير $q يجب أن يُمرَّر صراحةً كوسيط للـ Closure حتى يتم التعرف عليه داخلها. الشكل الصحيح كما ذكرت هو:
$questions = Question::with(['answers' => function ($q) { $q->inRandomOrder(); }])->inRandomOrder()->get();شكرًا لك على المتابعة الدقيقة، سأقوم بتصحيح الكود في المقال.