مساعد قاعدة بيانات GreenPig

  1. دخول
  2. اتصال المكتبة
  3. حيث الطبقة
  4. تاريخ الصف
  5. استعلام فئة

╔═══╗╔═══╗╔═══╗╔═══╗╔╗─╔╗────╔═══╗╔══╗╔═══╗ ║╔══╝║╔═╗║║╔══╝║╔══╝║╚═╝║────║╔═╗║╚╗╔╝║╔══╝ ║║╔═╗║╚═╝║║╚══╗║╚══╗║╔╗─║────║╚═╝║─║║─║║╔═╗ ║║╚╗║║╔╗╔╝║╔══╝║╔══╝║║╚╗║────║╔══╝─║║─║║╚╗║ ║╚═╝║║║║║─║╚══╗║╚══╗║║─║║────║║───╔╝╚╗║╚═╝║ ╚═══╝╚╝╚╝─╚═══╝╚═══╝╚╝─╚╝────╚╝───╚══╝╚═══╝ 5HHHG HH HHHHHHH 9HHHA HHHHHHHH5 HHHHHHHHHHHHHHHHHH 9HHHHH5 5HHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHH ;HHHHHHHHHHHHHHHHHHHHHHHHHHA H2 HHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHH9 HHHHHHHHHHHHHHHHHHHHHHH AHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHH9 iHS HHHHHHHHHHHHHHHHHHHHHHhh HHHHHHHHHHHHHHHHHH AA HHHHHHHHHHHHHH3 &H Hi HS Hr & H& H& Hi 

دخول


أريد أن أخبركم عن تطور مكتبتي الصغيرة في php. ما المهام التي تحلها؟ لماذا قررت أن أكتبها ولماذا يمكن أن تكون مفيدة لك؟ حسنا حاول الإجابة على هذه الأسئلة.


GreenPig (يشار إليها فيما يلي باسم GP ) هو مساعد قاعدة بيانات صغير يمكنه إكمال وظيفة أي إطار php تستخدمه.


مثل أي أداة GP ، يتم شحذها لحل بعض المشاكل. سيكون من المفيد لك إذا كنت تفضل كتابة استعلامات قاعدة البيانات في sql الخالص ولا تستخدم السجل النشط والتقنيات المشابهة الأخرى. على سبيل المثال ، لدينا قاعدة بيانات أوراكل في العمل وغالبًا ما تشغل الاستعلامات عدة شاشات مع العشرات من الصلات ، وما زالت دالات plsql ، الاتحاد الكل ، وما إلى ذلك تستخدم. وما إلى ذلك ، لذلك لم يتبق شيء سوى كتابة الاستعلامات في sql النقي.


ولكن مع هذا النهج ، يطرح السؤال التالي: كيفية إنشاء جزء من استعلام sql عندما يبحث المستخدمون عن معلومات؟ يهدف GP ، أولاً وقبل كل شيء ، إلى تجميع مناسب عن طريق php عند طلب أي تعقيد.


ولكن ما الذي دفعني إلى كتابة هذه المكتبة (باستثناء ، بالطبع ، للحصول على تجربة ممتعة)؟ هذه ثلاثة أشياء:


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


فيما يلي مثال لعينة قاعدة البيانات القياسية:
 [ [0] => [ ['id'] => 1 ['type'] => 'car' ['val_id'] => 1 ['name'] => ' ()' ['value'] => 790 ], [1] => [ ['id'] => 1 ['type'] => 'car' ['val_id'] => 2 ['name'] => '   ' ['value'] => 24 ], [2] => [ ['id'] => 1 ['type'] => 'car' ['val_id'] => 3 ['name'] => ' ' ['value'] => 75 ], [3] => [ ['id'] => 4 ['type'] => 'phone' ['val_id'] => 10 ['name'] => ' ' ['value'] => 5 ], [4] => [ ['id'] => 4 ['type'] => 'phone' ['val_id'] => 8 ['name'] => ' ()' ['value'] => 0.12 ], [5] => [ ['id'] => 4 ['type'] => 'phone' ['val_id'] => 9 ['name'] => '   ' ['value'] => 1 ], [6] => [ ['id'] => 4 ['type'] => 'phone' ['val_id'] => 10 ['name'] => ' ' ['value'] => 5 ], [7] => [ ['id'] => 4 ['type'] => 'phone' ['val_id'] => 8 ['name'] => ' ()' ['value'] => 0.12 ], [8] => [ ['id'] => 4 ['type'] => 'phone' ['val_id'] => 9 ['name'] => '   ' ['value'] => 1 ], [9] => [ ['id'] => 4 ['type'] => 'phone' ['val_id'] => 10 ['name'] => ' ' ['value'] => 5 ], [10] => [ ['id'] => 4 ['type'] => 'phone' ['val_id'] => 8 ['name'] => ' ()' ['value'] => 0.12 ], [11] => [ ['id'] => 4 ['type'] => 'phone' ['val_id'] => 9 ['name'] => '   ' ['value'] => 1 ], [12] => [ ['id'] => 1 ['type'] => 'car' ['val_id'] => 1 ['name'] => ' ()' ['value'] => 790 ], [13] => [ ['id'] => 1 ['type'] => 'car' ['val_id'] => 2 ['name'] => '   ' ['value'] => 24 ], [14] => [ ['id'] => 1 ['type'] => 'car' ['val_id'] => 3 ['name'] => ' ' ['value'] => 75 ], [15] => [ ['id'] => 1 ['type'] => 'car' ['val_id'] => 1 ['name'] => ' ()' ['value'] => 790 ], [16] => [ ['id'] => 1 ['type'] => 'car' ['val_id'] => 2 ['name'] => '   ' ['value'] => 24 ], [17] => [ ['id'] => 1 ['type'] => 'car' ['val_id'] => 3 ['name'] => ' ' ['value'] => 75 ] ] 

للحصول على صفيف يشبه الشجرة ، نحتاج إلى إحضار النتيجة إلى النموذج المرغوب فيه بأنفسنا ، أو تقديم استعلامات قاعدة بيانات N لكل منتج. وإذا كنا بحاجة إلى ترقيم الصفحات ، وحتى مع الفرز؟ GP قادر على حل هذه المشاكل. فيما يلي مثال لعينة مع GP :


 [ [1] => [ ['prod_type'] => 'car' ['properties'] => [ [1] => [ ['name'] => ' ()' ['value'] => 790 ] [2] => [ ['name'] => '   ' ['value'] => 24 ] [3] => [ ['name'] => ' ' ['value'] => 75 ] ] ] [4] => [ ['prod_type'] => 'phone' ['properties'] => [ [10] => [ ['name'] => ' ' ['value'] => 5 ] [8] => [ ['name'] => ' ()' ['value'] => 0.12 ] [9] => [ ['name'] => '   ' ['value'] => 1 ] ] ] ] 

وبالطبع في نفس الوقت ترقيم الصفحات والفرز المريح: ->pagination(1, 10)->sort('id') .


السبب الثاني ليس متكررًا جدًا ، ولكن مع ذلك يحدث (وفي هذه الحالة يكون السبب الرئيسي). إذا تم تخزين بعض الكيانات في قاعدة البيانات ، وخصائص هذه الكيانات ديناميكية وتعيينها من قبل المستخدمين ، فعندما تحتاج إلى البحث عن كيانات من خلال خصائصها ، سيتعين عليك إضافة (انضمام) نفس الجدول مع قيم الخصائص (عدة مرات حسب الاستخدام) الخصائص عند البحث). لذلك سوف يساعدك GP على توصيل جميع الجداول وإنشاء استعلام حيث مع وظيفة واحدة تقريبا. قرب نهاية المقالة سأحلل هذه الحالة بالتفصيل.


وأخيرًا ، يجب أن يعمل كل هذا مع كل من قاعدة بيانات Oracle و mySql. هناك أيضًا عدد من الميزات الموضحة في الوثائق.


قد اخترعت دراجة أخرى ، لكنني بحثت بضمير ولم أجد حلاً يناسبني. إذا كنت تعرف مكتبة تحل هذه المشكلات - يرجى كتابة التعليقات.


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


اتصال المكتبة


يمكن تثبيت المكتبة عن طريق الملحن: composer require falbin/green-pig-dao


ثم تحتاج إلى كتابة مصنع ستستخدم من خلاله هذه المكتبة.


حيث الطبقة


باستخدام هذه الفئة ، يمكنك إنشاء جزء من استعلام sql لأي تعقيد.


الجزء الذري من الطلب


النظر في أصغر ، الجزء الذري للاستعلام. يتم وصفه بواسطة صفيف: [, , ]
مثال: ['name', 'like', '%%']


  • العنصر الأول للصفيف هو مجرد سلسلة ، يتم إدراجه في استعلام sql بدون تغييرات ، وبالتالي ، يمكنك كتابة وظائف sql فيه. مثال: ['LOWER(name)', 'like', '%%']
  • العنصر الثاني هو أيضًا سلسلة يتم إدخالها في sql بدون تغييرات بين عاملين. يمكن أن يأخذ القيم التالية: = ،> ، <،> = ، <= ، <> ، مثل ، ليس مثل ، بين ، ليس بين ، في ، ليس في .
  • يمكن أن يكون العنصر الثالث من الصفيف إما نوع رقمي أو سلسلة. حيث سيقوم الفصل تلقائيًا باستبدال الاسم المستعار الذي تم إنشاؤه في استعلام sql.
  • عنصر صفيف مع مفتاح مزود. في بعض الأحيان يكون من الضروري إدخال القيمة في رمز sql دون تغييرات. على سبيل المثال ، لتطبيق وظائف. يمكن تحقيق ذلك عن طريق تحديد "sql" كمفتاح (للعنصر الثالث). مثال: ['LOWER(name)', 'like', 'sql' => "LOWER('$name')"]
  • عنصر الصفيف مع مفتاح الربط هو صفيف لتخزين الروابط. المثال أعلاه خاطئ من وجهة نظر الأمان. لا يمكنك إدراج متغيرات في sql - إنها حقنة مفرطة. لذلك ، في هذه الحالة ، ستحتاج إلى تحديد الأسماء المستعارة بنفسك ، على سبيل المثال مثل هذا: ['LOWER(name)', 'like', 'sql' => "LOWER(:name)", 'bind'=> ['name' => $name] ]
  • يمكن كتابة العامل في مثل هذا: ['curse', 'not in', [1, 3, 5]] . تقوم الطبقة بتحويل هذا الإدخال إلى رمز sql التالي: curse not in (:al_where_jCgWfr95kh, :al_where_mCqefr95kh, :al_where_jCfgfr9Gkh)
  • يمكن كتابة العبارة بين مثل هذا: ['curse', ' between', 1, 5] . تقوم الطبقة بتحويل مثل هذا الإدخال إلى رمز sql التالي: curse between :al_where_Pi4CRr4xNn and :al_where_WiPPS4NKiG
    لكن كن حذرًا ، إذا كان العنصران الثالث والرابع للصفيف عبارة عن سلاسل ، فسيتم تطبيق منطق خاص. في هذه الحالة ، يُعتقد أن التحديد من نطاق تاريخ ، وبالتالي ، تُستخدم دالة sql المتمثلة في إرسال سلسلة إلى تاريخ. وظيفة التحويل إلى تاريخ (mySql و Oracle لها تواريخ مختلفة) ويتم أخذ معلماتها من مجموعة من الإعدادات (أكثر في الوثائق). سيتم تحويل الصفيف ['build_date', 'between', '01.01.2016', '01.01.2019'] إلى sql: build_date between TO_DATE(:al_where_fkD7nZg5lU, 'dd.mm.yyyy hh24:mi::ss') and TO_DATE(:al_where_LdyVRznPF8, 'dd.mm.yyyy hh24:mi::ss')

استفسارات معقدة


لنقم بإنشاء مثيل للفئة خلال المصنع: $wh = GP::where();


للإشارة إلى الاتصال المنطقي بين "الأجزاء الذرية" للطلب ، يجب عليك استخدام linkAnd() أو linkOr() . مثال:


 // sql: (old > 18 and old < 50) $wh->linkAnd([ ['old', '<', 18], ['old', '>', 50] ]); // sql: (old < 18 or old > 50) $wh->linkOr([ ['old', '<', 18], ['old', '>', 50] ]); 

عند استخدام وظائف linkAnd / linkOr ، يتم تخزين جميع البيانات داخل مثيل فئة Where - $ wh. أيضا ، جميع "الأجزاء الذرية" المشار إليها في وظيفة بين قوسين .


يمكن وصف Sql بأي تعقيد من خلال ثلاث وظائف: linkAnd(), linkOr(), getRaw() . النظر في مثال:


 // sql: curse = 1 and (old < 18 or old > 50) $wh->linkAnd([ ['curse', '=', 1], $wh->linkOr([ ['old', '<', 18], ['old', '>', 50] ])->getRaw() ]); 

تحتوي فئة Where على متغير خاص يخزّن التعبير الخام. تقوم linkAnd() و linkOr() بالكتابة فوق هذا المتغير ، لذلك ، عند إنشاء تعبير منطقي ، يتم linkOr() الطرق معًا ، ويحتوي المتغير مع تعبير raw على بيانات تم الحصول عليها من الطريقة المنفذة الأخيرة.


الانضمام إلى الدرجة


Join عبارة عن فئة تنشئ جزءًا من كود sql. لنقم بإنشاء مثيل للفصل من خلال المصنع: $jn = GP::leftJoin('coursework', 'student_id', 's.id') ، حيث:


  • الدورات الدراسية هي الجدول الذي سننضم إليه.
  • student_id - عمود مع مفتاح خارجي من جدول الدورات الدراسية .
  • s.id - عمود الجدول الذي يجب أن يكون الرابط مكتوبًا به مع الاسم المستعار للجدول (في هذه الحالة ، يكون الاسم المستعار للجدول هو s).

تم إنشاء sql: left JOIN coursework coursework_joM9YuTTfW ON coursework_joM9YuTTfW.student_id = s.id


عند إنشاء مثيل للفئة ، قمنا بالفعل بشرح شرط الانضمام إلى الجداول ، ولكن قد يكون من الضروري تحسين وتوسيع الشرط. ستساعدك وظائف linkAnd / linkOr على القيام بذلك: $jn->linkAnd(['semester_number', '>', 2])


تم إنشاء sql: inner JOIN coursework coursework_Nd1n5T7c0r ON coursework_Nd1n5T7c0r.student_id = s.id and (semester_number > :al_where_M1kEcHzZyy)


إذا كان هناك العديد من الجداول للانضمام ، يمكنك دمجها في فئة: CollectionJoin .


استعلام فئة


هذه هي الفئة الرئيسية للعمل مع قاعدة البيانات ، من خلالها يوجد اختيار وتسجيل وتحديث وحذف البيانات. يمكنك أيضًا إجراء معالجة معينة للبيانات التي تم الحصول عليها من قاعدة البيانات.


النظر في مثال نموذجي.


لنقم بإنشاء مثيل للفئة خلال المصنع: $qr = GP::query();


سنقوم الآن بتعيين قالب sql ، واستبدل القيم اللازمة للسيناريو المحدد في قالب sql ونقول إننا نريد الحصول على سجل واحد ، وتحديدا البيانات من العمود average_mark .


 $rez = $qr->sql("select /*select*/ from student s inner join mark m on s.id = m.student_id inner join lesson l on l.id = m.lesson_id /*where*/ /*group*/") ->sqlPart('/*select*/', 's.name, avg(m.mark) average_mark', []) ->whereAnd('/*where*/', ['s.id', '=', 1]) ->sqlPart('/*group*/', 'group by s.name', []) ->one('average_mark'); 

النتيجة: 3,16666666666666666666666666666666666667


الاختيار من قاعدة بيانات مع المعلمات المتداخلة


الأهم من ذلك كله ، لم تتح لي الفرصة للحصول على تحديد من قاعدة البيانات في طريقة عرض شجرة ، مع الخصائص المرفقة. لذلك ، لدى مكتبة GP هذه الفرصة ، وعمق التعشيش غير محدود.


تعتمد أسهل طريقة للنظر في مبدأ التشغيل على مثال. للنظر ، نأخذ مخطط قاعدة البيانات التالية :



محتويات الجدول:



في كثير من الأحيان ، عند الاستعلام عن قاعدة بيانات ، فأنت ترغب في الحصول على إجابة شبيهة بالشجرة وليس إجابة مسطحة. على سبيل المثال ، عن طريق تشغيل هذا الاستعلام:


 SELECT s.id, s.name, c.id title_id, c.title FROM student s INNER JOIN coursework c ON c. student_id = s.id WHERE s.id = 3 

نحصل على نتيجة مسطحة:


 [ 0 => [ 'id' => 3, 'name' => '', 'title_id' => 6, 'title' => '«»    ', ], 1=> [ 'id' => 3, 'name' => '', 'title_id' => 7, 'title' => '  ' ] ] 

باستخدام GP ، يمكنك الحصول على هذه النتيجة:


 [ 3 => [ 'name' => '', 'courseworks' => [ 6 => ['title' => '«»    '], 7 => ['title' => '  '] ] ] ] 

لتحقيق هذه النتيجة ، تحتاج إلى تمرير صفيف بخيارات إلى الدالة all (تقوم الدالة بإرجاع جميع أسطر الاستعلام):


 all([ 'id'=> 'pk', 'name' => 'name', 'courseworks' => [ 'title_id' => 'pk', 'title' => 'title' ] ]) 

مجموعة $option في مجمع الوظائف ( $option ، $ rawData) وكلها ( $options ) مبنية وفقًا للقواعد التالية:


  • مفاتيح الصفيف - أسماء الأعمدة. عناصر المصفوفة - أسماء جديدة للأعمدة ، يمكنك إدخال الاسم القديم.
  • هناك كلمة محجوزة واحدة لقيم الصفيف - pk . تقول أنه سيتم تجميع البيانات حسب هذا العمود (مفتاح الصفيف هو اسم العمود).
  • في كل مستوى يجب أن يكون هناك pk واحد فقط.
  • في المصفوفة المجمعة (الناتجة) ، سيتم استخدام القيم من العمود الذي تم تعريفه بواسطة pk كمفاتيح.
  • إذا كان من الضروري وضع مستوى أقل من جزء من الأعمدة ، فسيتم استخدام اسم جديد تم اختراعه كمفتاح صفيف ، وسيتم استخدام صفيف تم إنشاؤه وفقًا للقواعد الموضحة أعلاه كقيمة.

النظر في مثال أكثر تعقيدا. لنفترض أننا بحاجة إلى تزويد جميع الطلاب باسم الدورات الدراسية الخاصة بهم ومع جميع الدرجات في جميع المواد الدراسية. نود الحصول عليها ليس بشكل مسطح ، ولكن بشكل شجرة ، دون تكرار. أدناه هو الاستعلام المطلوب إلى قاعدة البيانات والنتيجة.


 SELECT s.id student_id, s.name student_name, s.semester_number, c.id coursework_id, c.semester_number coursework_semester, c.title coursework_title, l.id lesson_id, l.name lesson, m.id mark_id, m.mark FROM student s LEFT JOIN coursework c ON c.student_id = s.id LEFT JOIN mark m ON m.student_id = s.id LEFT JOIN lesson l ON l.id = m.lesson_id ORDER BY s.id, c.id, l.id, m.id 

النتيجة لا تناسبنا:



لتحقيق المهمة ، تحتاج إلى كتابة صفيف $option التالي:


 $option = [ 'student_id' => 'pk', 'student_name' => 'name', 'courseworks' => [ 'coursework_semester' => 'pk', 'coursework_title' => 'title' ], 'lessons' => [ 'lesson_id' => 'pk', 'lesson' => 'lesson', 'marks' => [ 'mark_id' => 'pk', 'mark' => 'mark' ] ] ]; 

استعلام قاعدة البيانات:


 //    Query   .       // (    yii2) $qr = Yii::$app->gp->query( "SELECT s.id student_id, s.name student_name, s.semester_number, c.id coursework_id, c.semester_number coursework_semester, c.title coursework_title, l.id lesson_id, l.name lesson, m.id mark_id, m.mark FROM student s LEFT JOIN coursework c ON c.student_id = s.id LEFT JOIN mark m ON m.student_id = s.id LEFT JOIN lesson l ON l.id = m.lesson_id ORDER BY s.id, c.id, l.id, m.id"); //   1,  2   3  //  1 $result = $qr->all($option); //  2 $result = $qr->aggregator($option, $qr->all()); //  3 $qr->all(); $result = $qr->aggregator($option, $qr->rawData()); 

يمكن أن تقوم دالة aggregator بمعالجة أي صفيف بهيكل مشابه لنتيجة استعلام قاعدة البيانات ، وفقًا للقواعد الموضحة في $option .


يحتوي متغير $result على البيانات التالية:


 [ 1 => [ 'name' => '', 'courseworks' => [ 1 => ['title' => '    '], ], 'lessons' => [ 1 => [ 'lesson' => '', 'marks' => [ 1 => ['mark' => 3], 2 => ['mark' => 4] ] ], 2 => [ 'lesson' => '', 'marks' => [ 3 => ['mark' => 2], 4 => ['mark' => 2], 5 => ['mark' => 3] ] ], 4 => [ 'lesson' => '', 'marks' => [ 6 => ['mark' => 5] ] ] ] ], 3 => [ 'name' => '', 'courseworks' => [ 1 => ['title' => '«»    '], 2 => ['title' => '  '] ], 'lessons' => [ 1 => [ 'lesson' => '', 'marks' => [ 17 => ['mark' => 5] ] ], 2 => [ 'lesson' => '', 'marks' => [ 18 => ['mark' => 2] ] ], 3 => [ 'lesson' => '-', 'marks' => [ 20 => ['mark' => 4] ] ], 4 => [ 'lesson' => '', 'marks' => [ 16 => ['mark' => 2], 19 => ['mark' => 3] ] ], ] ] ] 

بالمناسبة ، مع ترقيم الصفحات مع الاستعلام المجمع ، فقط الجزء العلوي ، تعتبر معظم البيانات الأساسية. في المثال أعلاه ، سيكون هناك سطرين فقط لترقيم الصفحات.


اتحاد متعددة مع نفسك في اسم البحث


كما كتبت سابقًا ، تتمثل المهمة الرئيسية لمكتبتي في تبسيط إنشاء الأجزاء التي توجد فيها استعلامات محددة. في هذه الحالة ، قد نحتاج إلى الانضمام مرارًا وتكرارًا إلى نفس الجدول لمعرفة أين الاستعلام؟ أحد الخيارات هو عندما يكون لدينا منتج معين لا تُعرف خصائصه مقدمًا وسيضافه المستخدمون ، ويجب أن تُمنح الفرصة للبحث عن المنتجات بواسطة هذه الخصائص الديناميكية. أسهل طريقة لشرح مع مثال مبسط.


لنفترض أن لدينا متجرًا على الإنترنت يبيع مكونات الكمبيوتر ، وليس لدينا تشكيلة صارمة ، وسنقوم بشراء مكون أو آخر بشكل دوري. لكننا نود وصف جميع منتجاتنا ككيان واحد والبحث عن جميع المنتجات. لذلك ، ما هي الكيانات التي يمكن تمييزها من وجهة نظر منطق الأعمال:


  1. السلع. أهم كيان يتمحور حوله كل شيء.
  2. نوع المنتج يمكن تمثيل ذلك كخاصية الجذر لجميع خصائص المنتج الأخرى. على سبيل المثال ، في متجرنا الصغير هو فقط: RAM و SSD و HDD.
  3. خصائص المنتج. في تطبيقنا ، يمكن تطبيق أي خاصية على أي نوع من المنتجات ، ويظل الخيار على ضمير المدير. في متجرنا ، صنع المديرون 3 خصائص فقط: حجم الذاكرة ، عامل الشكل و DDR.
  4. قيمة البضاعة. القيمة التي سيدفعها المشتري في البحث.

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



على سبيل المثال ، لدينا منتج: 16 GB DDR 3 RAM . في الرسم البياني ، يمكن عرض هذا على النحو التالي:



هيكل وبيانات قاعدة البيانات واضحة للعيان في الشكل التالي:



كما نرى من المخطط ، يتم تخزين جميع قيم جميع الخصائص في جدول واحد (بالمناسبة ، في نسختنا المبسطة ، تحتوي جميع الخصائص على قيم رقمية). لذلك ، إذا كنا نريد البحث في وقت واحد عن العديد من الخصائص مع مجموعة من AND ، فسنحصل على تحديد فارغ.


على سبيل المثال ، يبحث المشتري عن منتجات مناسبة لمثل هذا الطلب: يجب أن يكون حجم الذاكرة أكثر من 10 جيجابايت وأن يكون عامل الشكل 2.5 بوصة . إذا كتبنا sql كما هو موضح أدناه ، فسوف نحصل على تحديد فارغ:


 select * from product p inner join val v on v.product_id = p.id where (v.property_id = 1 and v.value > 10) AND (v.property_id = 3 and v.value = 2.5) 

نظرًا لأنه يتم تخزين قيم جميع الخصائص في جدول واحد ، للبحث عن العديد من الخصائص ، يجب عليك الانضمام إلى val val لكل خاصية يتم البحث عنها. ولكن هناك فارق بسيط ، انضمام ينضم الجداول "أفقيا" (لكلمة الاتحاد جميع ينضم "عموديا") ، وفيما يلي مثال:



هذه النتيجة لا تناسبنا ، نود أن نرى كل القيم في عمود واحد. للقيام بذلك ، تحتاج إلى الانضمام إلى جدول val 1 مرات أكثر من الخصائص التي يتم تنفيذ البحث.



نحن على وشك إنشاء استعلام SQL تلقائيًا. لنلقِ نظرة على الوظيفة
whereWithJoin ($aliasJoin, $options, $aliasWhere, $where) ، والتي ستقوم بكل العمل:


  • $ aliasJoin - اسم مستعار في القالب الأساسي ، بدلاً من استبدال جزء sql مع الصلات.
  • خيارات $ - صفيف مع أوصاف القواعد لإنشاء جزء الصلة.
  • $ aliasWhere - اسم مستعار في القالب الأساسي ، والذي يحل محل جزء sql بدلاً منه.
  • $ أين هو مثيل لفئة أين.

دعونا نلقي نظرة على مثال: whereWithJoin('/*join*/', $options, '/*where*/', $wh) .


أولاً ، قم بإنشاء متغير خيارات $ : $options = ['v' => ['val', 'product_id', 'p.id']];


الخامس هو الاسم المستعار للجدول. إذا تم العثور على هذا الاسم المستعار في $ wh ، فسيتم ربط جدول val جديد بواسطة join (حيث يكون product_id هو المفتاح الخارجي لجدول val ، و p.id هو المفتاح الأساسي للجدول مع الاسم المستعار p ) ، ويتم إنشاء اسم مستعار جديد ويتم إنشاء هذا الاسم المستعار له سوف تحل محل الخامس في أين.


$ wh هو مثيل لفئة Where. نحن نقوم بتشكيل نفس الطلب: يجب أن تكون الذاكرة أكبر من 10 جيجابايت وأن يكون عامل الشكل 2.5 بوصة.


 $wh->linkAnd([ $wh->linkAnd([ ['v.property_id', '=', 1], ['v.value', '>', 10] ])->getRaw(), //    $wh->linkAnd([ ['v.property_id', '=', 3], ['v.value', '=', 2.5] ])->getRaw(),//    ]); 

عند إنشاء طلب مكان ، من الضروري أن يلتف الجزء بخاصية المعرف وقيمته بين قوسين ، وهذا يخبر الدالة whereWithJoin() بأن الاسم المستعار للجدول سيكون هو نفسه في هذا الجزء.


 $qr->sql("select p.id, t.name type_name, pr.id prop_id, pr.name prop_name, v.id val_id, v.value from product p inner join type t on t.id = p.type_id inner join val v on v.product_id = p.id inner join properties pr on pr.id = v.property_id /*join*/ /*where*/") ->whereWithJoin('/*join*/', $options, '/*where*/', $wh) //     . ->all([ 'id' => 'pk', 'type_name' => 'type', 'properties' => [ 'prop_id' => 'pk', 'prop_name' => 'name', 'values' => [ 'val_id' => 'pk', 'value' => 'val' ] ] ]); 

ننظر إلى وقت إنشاء SQL وربط ووقت تنفيذ الاستعلام: $qr->debugInfo() :


 [ [ 'type' => 'info', 'sql' => 'select p.id, t.name type_name, pr.id prop_id, pr.name prop_name, v.id val_id, v.value from product p inner join type t on t.id = p.type_id inner join val v on v.product_id = p.id inner join properties pr on pr.id = v.property_id inner JOIN val val_mIQWpnHhdQ ON val_mIQWpnHhdQ.product_id = p.id inner JOIN val val_J0uveMpwEM ON val_J0uveMpwEM.product_id = p.id WHERE ( val_mIQWpnHhdQ.property_id = :al_where_leV5QlmOZN and val_mIQWpnHhdQ.value > :al_where_ycleYAswIw ) and ( val_J0uveMpwEM.property_id = :al_where_dinxDraTOE and val_J0uveMpwEM.value = :al_where_wZJhUqs74i )', 'binds' => [ 'al_where_leV5QlmOZN' => 1, 'al_where_ycleYAswIw' => 10, 'al_where_dinxDraTOE' => 3, 'al_where_wZJhUqs74i' => 2.5 ], 'timeQuery' => 0.0384588241577 ] ] 

$qr->rawData() :


 [ [ 'id' => 3, 'type_name' => 'SSD', 'prop_id' => 1, 'prop_name' => ' ', 'val_id' => 5, 'value' => 512 ], [ 'id' => 3, 'type_name' => 'SSD', 'prop_id' => 3, 'prop_name' => '-', 'val_id' => 6, 'value' => 2.5 ], [ 'id' => 4, 'type_name' => 'SSD', 'prop_id' => 1, 'prop_name' => ' ', 'val_id' => 7, 'value' => 256 ], [ 'id' => 4, 'type_name' => 'SSD', 'prop_id' => 3, 'prop_name' => '-', 'val_id' => 8, 'value' => 2.5 ], [ 'id' => 6, 'type_name' => 'HDD', 'prop_id' => 1, 'prop_name' => ' ', 'val_id' => 11, 'value' => 1024 ], [ 'id' => 6, 'type_name' => 'HDD', 'prop_id' => 3, 'prop_name' => '-', 'val_id' => 12, 'value' => 2.5 ] ] 

$qr->aggregateData() :


 [ 3 => [ 'type' => 'SSD', 'properties' => [ 1 => [ 'name' => ' ', 'values' => [ 5 => ['val' => 512] ] ], 3 => [ 'name' => '-', 'values' => [ 6 => ['val' => 2.5] ] ] ] ], 4 => [ 'type' => 'SSD', 'properties' => [ 1 => [ 'name' => ' ', 'values' => [ 7 => ['val' => 256] ] ], 3 => [ 'name' => '-', 'values' => [ 8 => ['val' => 2.5] ] ] ] ], 6 => [ 'type' => 'HDD', 'properties' => [ 1 => [ 'name' => ' ', 'values' => [ 11 => ['val' => 1024] ] ], 3 => [ 'name' => '-', 'values' => [ 12 => ['val' => 2.5] ] ] ] ] ] 

, , whereWithJoin() , .


whereWithJoin() , , n , m . n m 1 id . , AND .




GitHub .

Source: https://habr.com/ru/post/ar483458/


All Articles