نموذج قائمة المجاورة مع جدولين
-
22-09-2019 - |
سؤال
لذا أنا فكر في مشكلتي تتلخص في سؤالين:
كيف يمكنني بناء بنية شجرة يمكن اجتيازها في PHP عندما يتم تخزين الشجرة في MySQL (بين جدولين) باستخدام نهج نموذج قائمة المجاورة مع وضع الأداء في الاعتبار؟
ما هو النهج القابل للصيانة لعرض الشجرة في التنسيقات المطلوبة دون تكرار رمز اجتياز وتندرج المنطق مع عبارات IF/else والتبديل؟
فيما يلي المزيد من التفاصيل:
أنا أستخدم إطار Zend.
أنا أعمل مع استبيان. يتم تخزينه في قاعدة بيانات MySQL بين جدولين منفصلين: الأسئلة و Question_groups. يمتد كل جدول فئات zend_db_table_*. يتم تمثيل التسلسل الهرمي باستخدام نهج نموذج قائمة المجاورة.
أدرك أن المشكلات التي أركض فيها من المحتمل أن تكون بسبب حشو بنية شجرة في RDBMS ، لذا فأنا منفتح على البدائل. ومع ذلك ، أنا أيضًا أقوم بتخزين المجيبين في الاستبيان واستجاباتهم ، لذا فإن الأساليب البديلة ستحتاج إلى دعم ذلك.
يجب عرض الاستبيان بتنسيقات HTML المختلفة:
- كنموذج لإدخال الردود (باستخدام Zend_Form)
- كقائمة مرتبة (متداخلة) مع أسئلة (و بعض المجموعات) كروابط لعرض الإجابات حسب السؤال أو حسب المجموعة.
- كقائمة مرتبة (متداخلة) مع ردود ملحق بكل سؤال.
الأسئلة هي العقد الورقية ويمكن أن تحتوي مجموعات Question_groups على Question_groups و/أو الأسئلة الأخرى. مجتمعة ، هناك ما يزيد قليلاً عن 100 صف للمعالجة والعرض.
حاليًا ، لدي مساعد عرض يقوم بكل المعالجة باستخدام عودة لاسترداد أطفال Question_group (استعلام يؤدي الاتحاد بين الجدولين: QuestionGroup :: GetChildren ($ ID)). بالإضافة إلى ذلك عند عرض الاستبيان مع رد السؤال ، هناك حاجة إلى استفسارين إضافيين لاسترداد المدعى عليه وردته على كل سؤال.
على الرغم من أن وقت تحميل الصفحة ليس طويلاً جدًا ، فإن هذا النهج يبدو خاطئًا. لا تجعلني العواقب بالإضافة إلى استفسارات قواعد البيانات المتعددة لكل عقدة تقريبًا أشعر بالدفء والغموض في الداخل.
لقد حاولت عودة أقل والطرق العودية على صفيف الأشجار الكامل عادت من الاتحاد لبناء صفيف هرمي لاجتياز وعرض. ومع ذلك ، يبدو أن هذا ينهار نظرًا لوجود معرفات عقدة مكررة بسبب حقيقة أن المجموعات والأسئلة يتم تخزينها في جداول منفصلة. ربما أفتقد شيئًا هناك ...
حاليا ، المنطق لعرض الشجرة في التنسيقات المذكورة أعلاه هو فوضى تماما. أفضل عدم تكرار منطق اجتياز في كل مكان. ومع ذلك ، فإن الشرطية في كل مكان لا تنتج أكثر الكود القابل للصيانة أيضًا. لقد قرأت على الزائرين والديكور وبعض تكرارات PHP SPL ، لكنني ما زلت أشعر بأنني غير واضح حول كيفية عمل كل ذلك مع الفصول التي تمتد Zend_DB_Table و Zend_DB_Table_Rowset و Zend_DB_Table_Row. خاصة وأنني لم أحل المشكلة السابقة المتمثلة في بناء التسلسل الهرمي من قاعدة البيانات. سيكون من الجيد إضافة تنسيقات عرض جديدة (أو تعديل بتنسيقات موجودة) بسهولة إلى حد ما.
المحلول
تمنحك قائمة المجاورة تقليديًا
parent_id
عمود في كل صف يربط صفًا إلى الوالد المباشر. الparent_id
هو لاغ إذا كان الصف هو جذر الشجرة. لكن هذا يقودك إلى تشغيل العديد من استعلامات SQL ، وهو أمر مكلف.أضف عمودًا آخر
root_id
لذلك يعرف كل صف الشجرة التي تنتمي إليها. وبهذه الطريقة يمكنك جلب جميع العقد من شجرة معينة مع استعلام SQL واحد. أضف طريقة إلىTable
الفصل لجلب أRowset
بواسطة معرف جذر الشجرة.class QuestionGroups extends Zend_Db_Table_Abstract { protected $_rowClass = 'QuestionGroup'; protected $_rowsetClass = 'QuestionGroupSet'; protected function fetchTreeByRootId($root_id) { $rowset = $this->fetchAll($this ->select() ->where('root_id = ?', $root_id) ->order('id'); ); $rowset->initTree(); return $rowset; } }
اكتب فئة مخصصة تمتد
Zend_Db_Table_Row
وكتابة وظائف لاسترداد والد الصف المعطى وكذلك أRowset
من أطفالها. الRow
يجب أن يحتوي الفصل على كائنات بيانات محمية للإشارة إلى الوالد ومجموعة من الأطفال. أRow
يمكن أن يكون للكائن أيضًا ملفgetLevel()
وظيفة وgetAncestorsRowset()
وظيفة لفتات الخبز.class QuestionGroup extends Zend_Db_Table_Row_Abstract { protected $_children = array(); protected $_parent = null; protected $_level = null; public function setParent(Zend_Db_Table_Row_Abstract $parent) { $this->_parent = $parent; } public function getParent() { return $this->_parent; } public function addChild(Zend_Db_Table_Row_Abstract $child) { $this->_children[] = $child; } public function getChildren() { return $this->_children; } public function getLevel() {} public function getAncestors() {} }
اكتب فئة مخصصة تمتد
Zend_Db_Table_Rowset
هذا له وظيفة للتكرار فوق الصفوف في الصفوف ، مما يضع مراجع الوالدين والأطفال بحيث يمكنك بعد ذلك اجتيازهم كشجرة. أيضاRowset
يجب أن يكونgetRootRow()
وظيفة.class QuestionGroupSet extends Zend_Db_Table_Rowset_Abstract { protected $_root = null; protected function getRootRow() { return $this->_root; } public function initTree() { $rows = array(); $children = array(); foreach ($this as $row) { $rows[$row->id] = $row; if ($row->parent_id) { $row->setParent($rows[$row->parent_id]); $rows[$row->parent_id]->addChild($row); } else { $this->_root = $row; } } } }
الآن يمكنك الاتصال getRootRow()
على صفوف ، ويعيد عقدة الجذر. بمجرد حصولك على عقدة الجذر ، يمكنك الاتصال getChildren()
وحلق عليها. ثم يمكنك الاتصال getChildren()
أيضا على أي من هؤلاء الأطفال المتوسطة ، وإخراج شجرة الشجرة بشكل متكرر بأي شكل تريد.