
- بواسطة x32x01 ||
بص يا معلم، لو بتشتغل على Laravel، لازم تاخد بالك من حاجة مهمة جدًا!
فيه ناس بتستخدم دالة whereHas() بطريقة متداخلة جوه بعضها... ودي مصيبة بتضرب أداء التطبيق في الأرض
عن تجربة شخصية - لو عندك بيانات كتير، الكود ده هيخلّي السيرفر ينهج
يعني إيه whereHas أصلاً؟
دالة
يعني مثلًا، لو عندك جدول الاشتراكات (subscriptions) وجواه علاقات مع الخصائص (features) والأسعار (prices)
تقدر تستخدمها عشان تجيب الاشتراكات اللي فيها أسعار معينة أو خصائص محددة.
بس المشكلة مش في الدالة نفسها... المشكلة في التداخل!
الكود ده شكله بسيط... لكن هو اللي بيولّع السيرفر 
اللي بيحصل هنا إن Laravel بيعمل استعلام فرعي (Subquery) لكل سطر تقريبًا!
وده معناه إنك لو عندك آلاف السجلات… هيعمل آلاف الاستعلامات!
فيه سببين رئيسيين:
1. تكرار الاستعلامات الفرعية
كل مرة بيعمل whereHas متداخلة، بيتنفذ استعلام جديد في الخلفية.
وده بيزود الحمل على قاعدة البيانات بشكل كبير جدًا، خصوصًا لو عندك آلاف الصفوف.
2. Full Table Scan
Laravel بيعمل مسح كامل لجدول الأسعار (prices) عشان يلاقي السجلات المناسبة.
تخيل معايا جدول فيه مليون صف، هيفضل يقرأهم كلهم واحد واحد
وده معناه بطء رهيب جدًا في الأداء.
Laravel عندها مميزات تخليك توصل لنفس النتيجة بدون وجع دماغ ولا تبطئة.
بدل ما تعمل whereHas جوه whereHas، استخدم العلاقات المترابطة بالشكل ده:
هنا Laravel بيترجم العلاقة بشكل مباشر بدون ما يعمل استعلام متداخل لكل سجل.
النتيجة؟
أداء أسرع بكتير وqueries أقل بكتير!
لو هدفك إنك تجيب البيانات كلها بعلاقاتها في استعلام واحد، استخدم with() 
الميزة هنا إن Laravel بيستخدم Eager Loading بدل Lazy Loading
وده يعني إنه بيحمّل البيانات كلها في استعلام واحد منظم وفعال
لو بتتعامل مع Eloquent ORM في Laravel:
افتكر دايمًا
الكود الحلو مش اللي بيشتغل بس… الكود الحلو هو اللي بيشتغل بسرعة وكفاءة
استخدام whereHas مش غلط، لكن التداخل فيها بيكلفك كتير من ناحية الأداء 
بدلها بالعلاقات المباشرة أو استخدم with() لما تحتاج تحمّل البيانات مسبقًا.
الموضوع بسيط بس فرقه كبير جدًا في السرعة والأداء
فيه ناس بتستخدم دالة whereHas() بطريقة متداخلة جوه بعضها... ودي مصيبة بتضرب أداء التطبيق في الأرض

عن تجربة شخصية - لو عندك بيانات كتير، الكود ده هيخلّي السيرفر ينهج

يعني إيه whereHas أصلاً؟
دالة whereHas()
في Laravel Eloquent بتستخدمها لما تحب تجيب البيانات اللي ليها علاقة بشروط معينة من جداول تانية.يعني مثلًا، لو عندك جدول الاشتراكات (subscriptions) وجواه علاقات مع الخصائص (features) والأسعار (prices)
تقدر تستخدمها عشان تجيب الاشتراكات اللي فيها أسعار معينة أو خصائص محددة.
بس المشكلة مش في الدالة نفسها... المشكلة في التداخل!

مثال على الكارثة
الكود ده شكله بسيط... لكن هو اللي بيولّع السيرفر 
PHP:
Subscription::whereHas('features', function ($query) {
$query->whereHas('prices', function ($query) {
$query->where('amount', '>', 100);
});
})->get();
وده معناه إنك لو عندك آلاف السجلات… هيعمل آلاف الاستعلامات!

ليه الكود ده بيبوّظ الأداء؟
فيه سببين رئيسيين:1. تكرار الاستعلامات الفرعية
كل مرة بيعمل whereHas متداخلة، بيتنفذ استعلام جديد في الخلفية.وده بيزود الحمل على قاعدة البيانات بشكل كبير جدًا، خصوصًا لو عندك آلاف الصفوف.
2. Full Table Scan
Laravel بيعمل مسح كامل لجدول الأسعار (prices) عشان يلاقي السجلات المناسبة.تخيل معايا جدول فيه مليون صف، هيفضل يقرأهم كلهم واحد واحد

وده معناه بطء رهيب جدًا في الأداء.
الحل؟ استخدم العلاقات بطريقة ذكية!
Laravel عندها مميزات تخليك توصل لنفس النتيجة بدون وجع دماغ ولا تبطئة.
الطريقة الصحيحة: العلاقات المترابطة
بدل ما تعمل whereHas جوه whereHas، استخدم العلاقات المترابطة بالشكل ده: PHP:
Subscription::whereHas('features.prices', function ($query) {
$query->where('amount', '>', 100);
})->get();
هنا Laravel بيترجم العلاقة بشكل مباشر بدون ما يعمل استعلام متداخل لكل سجل.
النتيجة؟

طريقة تانية قوية: استخدم with() لتحميل البيانات مسبقًا
لو هدفك إنك تجيب البيانات كلها بعلاقاتها في استعلام واحد، استخدم with() 
PHP:
Subscription::with(['features.prices' => function ($query) {
$query->where('amount', '>', 100);
}])->get();
الميزة هنا إن Laravel بيستخدم Eager Loading بدل Lazy Loading
وده يعني إنه بيحمّل البيانات كلها في استعلام واحد منظم وفعال

الفرق بين whereHas و with باختصار
الخاصية | whereHas | with |
---|---|---|
الوظيفة | تصفية السجلات بناءً على علاقة | تحميل العلاقات مسبقًا |
الأداء | أبطأ لو متداخلة | أسرع ومناسب للبيانات الكبيرة |
الاستخدام | لما تكون عايز فلترة دقيقة | لما تكون عايز بيانات كاملة بعلاقاتها |
نصيحة للمطورين
لو بتتعامل مع Eloquent ORM في Laravel:استخدم relationships المتسلسلة بدل التداخل المتكرر.
فعّل الـ query logging وشوف الاستعلامات اللي بتتنفذ فعليًا.
جرّب دايمًا تحسّن الكود بالأداء مش بالشكل.
افتكر دايمًا

الكود الحلو مش اللي بيشتغل بس… الكود الحلو هو اللي بيشتغل بسرعة وكفاءة

خلاصة الكلام
استخدام whereHas مش غلط، لكن التداخل فيها بيكلفك كتير من ناحية الأداء 
بدلها بالعلاقات المباشرة أو استخدم with() لما تحتاج تحمّل البيانات مسبقًا.
الموضوع بسيط بس فرقه كبير جدًا في السرعة والأداء

التعديل الأخير: