كيفية استخراج SQL الخام من Query Builder في PHP: 3 طرق عملية

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

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

ابدأ الدورة

"دكتور، Query Builder الخاص بي يولد شيئًا غريبًا..."

\n

موقف مألوف: تكتب استعلامًا متسلسلًا أنيقًا باستخدام Query Builder، وتكون متأكدًا أن كل شيء يعمل بشكل مثالي، ولكن الناتج إما خطأ أو بيانات غير صحيحة. أول ما يتبادر إلى ذهن أي مطور PHP: "أرني ما الذي تولده بالفعل!"

\n

السؤال "How do I get the query builder to output its raw SQL query as a string?" هو أحد أكثر الأسئلة شيوعًا على Stack Overflow (آلاف المشاهدات وعشرات الإجابات). وهذا ليس من قبيل الصدفة. القدرة على الحصول بسرعة على SQL نقي هي قوة خارقة توفر ساعات من التصحيح.

\n

في هذه المقالة، سنشرح 3 طرق عملية لأطر PHP (Laravel، Doctrine، CakePHP)، وسنعرض أمثلة كود حقيقية ونشرح متى نستخدم كل طريقة. هيا بنا!

\n\n

لماذا نحتاج هذا؟ (باختصار وبشكل مباشر)

\n
    \n
  • التصحيح — لفهم سبب عدم إرجاع الاستعلام للبيانات الصحيحة.
  • \n
  • تحسين الأداء — لرؤية JOINs و WHEREs التي يتم تنفيذها فعليًا.
  • \n
  • التعلم — المبتدئون غالبًا لا يفهمون كيف يترجم Query Builder الطرق إلى SQL.
  • \n
  • تسجيل الأحداث — لحفظ الاستعلام الخام للتدقيق أو إعادة التشغيل.
  • \n
\n

مستعدون؟ إذن ننتقل إلى الكود!

\n\n

الطريقة 1: Laravel Eloquent / Query Builder — الطريقة الكلاسيكية

\n

في Laravel، هناك عدة طرق، لكن الأكثر موثوقية هو استخدام toSql() مع getBindings(). لماذا معًا؟ لأن toSql() يعرض الاستعلام مع العناصر النائبة ?، وبدون استبدال القيم يكون عديم الفائدة.

\n\n

مثال بسيط: SELECT مع WHERE

\n
use App\\Models\\User;\n\n$query = User::where('email', 'like', '%@gmail.com')\n             ->where('active', 1)\n             ->orderBy('created_at', 'desc');\n\n// الحصول على SQL مع العناصر النائبة\n$sql = $query->toSql();\n$bindings = $query->getBindings();\n\n// تجميع كل شيء في شكل قابل للقراءة\n$fullSql = vsprintf(str_replace('?', '%s', $sql), $bindings);\n\necho $fullSql;\n// النتيجة:\n// SELECT * FROM users WHERE email LIKE '%@gmail.com' AND active = 1 ORDER BY created_at DESC
\n

هام: vsprintf ليس حلاً سحريًا. لأنواع البيانات المعقدة (مثل NULL أو المصفوفات) من الأفضل استخدام Str::replaceArray() أو الحزمة laravel-query-builder-debug.

\n\n

نسخة متقدمة: مع join و whereIn

\n
$query = DB::table('orders')\n    ->join('users', 'orders.user_id', '=', 'users.id')\n    ->whereIn('orders.status', ['pending', 'processing'])\n    ->where('orders.total', '>', 100)\n    ->select('orders.*', 'users.name');\n\n$bindings = $query->getBindings();\n$sql = $query->toSql();\n\n// تنسيق جميل\n$fullSql = preg_replace_array('/\\?/', $bindings, $sql);\necho $fullSql;\n// SELECT orders.*, users.name FROM orders INNER JOIN users ON orders.user_id = users.id WHERE orders.status IN ('pending', 'processing') AND orders.total > 100
\n

نصيحة: استخدم preg_replace_array() — فهو أكثر أمانًا من vsprintf، خاصة عندما تحتوي البيانات على الرمز %.

\n\n

الطريقة 2: Doctrine DBAL — لمن يحبون الدقة

\n

Doctrine Query Builder (يُستخدم غالبًا في Symfony) هو وحش أكثر تعقيدًا. لا يوجد هنا toSql() جاهز، ولكن يوجد getSQL() و getParameters(). بالإضافة إلى ذلك، يجب مراعاة أنواع المعاملات.

\n\n

مثال أساسي

\n
use Doctrine\\DBAL\\Connection;\nuse Doctrine\\DBAL\\Query\\QueryBuilder;\n\n/** @var Connection $connection */\n$queryBuilder = $connection->createQueryBuilder();\n$queryBuilder\n    ->select('p.*', 'c.name as category_name')\n    ->from('products', 'p')\n    ->join('p', 'categories', 'c', 'p.category_id = c.id')\n    ->where('p.price > :minPrice')\n    ->andWhere('p.sto                

المدونات

توصيات الكتب