أعيد نشره من جوي موراكامي، لقد قرأت هذا المقال من قبل، وقد تم شرحه بوضوح تام.
سنواجه هذه المشكلة في العديد من الأماكن مثل تصنيف المنتجات والمنتديات الشجرية متعددة المستويات والقوائم البريدية وما إلى ذلك: كيفية تخزين البيانات المنظمة متعددة المستويات؟
في تطبيقات PHP، عادةً ما يكون تخزين البيانات الخلفية عبارة عن قاعدة بيانات علائقية يمكنها حفظ كميات كبيرة من البيانات وتوفير خدمات فعالة لاسترجاع البيانات وتحديثها. ومع ذلك، فإن الشكل الأساسي للبيانات العلائقية هو جدول متقاطع، وهو عبارة عن بنية مسطحة. إذا كنت تريد تخزين بنية شجرة متعددة المستويات في قاعدة بيانات علائقية، فأنت بحاجة إلى إجراء أعمال ترجمة معقولة. وبعد ذلك سأناقش معكم ما رأيت وسمعت وبعض التجارب العملية.
هناك أساسًا طريقتان شائعتان للتصميم لتخزين البيانات الهرمية في قواعد البيانات المسطحة:
نموذج القائمة المجاورة
تعديل خوارزمية اجتياز شجرة الطلب المسبق
أنا لست متخصصًا في الكمبيوتر، ولم أتعلم أي شيء عن هياكل البيانات، لذا قمت بترجمة هذين الاسمين حرفيًا. يرجى إعلامي إذا كنت مخطئًا.
قد يبدو هذان الأمران مخيفين، لكنهما في الواقع من السهل جدًا فهمهما. أستخدم هنا دليل طعام بسيطًا كبيانات نموذجية لدينا. هيكل البيانات لدينا هو مثل هذا:
طعام
|
|---فاكهة
|
|---أحمر
|
|.|--كرز
|
|---أصفر
|
|.--موز
|
|---لحم
|
|--لحم بقر
|
|--لحم الخنزير
من أجل رعاية عشاق PHP الذين لديهم فوضى في
الطعام الإنجليزي: الطعام
الفاكهة : الفاكهة
الأحمر: الأحمر
الكرز: الكرز
الأصفر: الأصفر
موز : موز
لحم : لحم
لحم البقر: لحم البقر
لحم الخنزير:
نموذج قائمة لحم الخنزير المجاور
هو نموذج نستخدمه غالبًا وقد تم تقديمه في العديد من البرامج التعليمية والكتب. نحن نصف بنية الشجرة بأكملها من خلال جدول مسطح عن طريق إضافة أصل سمة لكل عقدة لتمثيل العقدة الأصلية لهذه العقدة. ووفقاً لهذا المبدأ يمكن تحويل البيانات الموجودة في المثال إلى الجدول التالي:
+-----------------------+
|. اسم الوالد |
+-----------------------+
|
|. فاكهة |
|. فاكهة |
|
|. فاكهة |
|. كرز أحمر |
|. فاكهة |
|.أصفر |
|.لحوم |
|. لحم |
|. لحم |
+-----------------------+
نرى أن الكمثرى هي عقدة فرعية للعقدة الخضراء، والأخضر هي عقدة فرعية للفاكهة. لا تحتوي العقدة الجذرية "Food" على عقدة أصل. لوصف هذه المشكلة ببساطة، يتم استخدام الاسم فقط لتمثيل سجل في هذا المثال. في قاعدة البيانات الفعلية، تحتاج إلى استخدام معرف رقمي لتحديد كل عقدة. من المحتمل أن تبدو بنية جدول قاعدة البيانات كما يلي: المعرف، Parent_id، الاسم، الوصف. باستخدام هذا الجدول، يمكننا حفظ بنية الشجرة متعددة المستويات بالكامل من خلال قاعدة البيانات.
عرض شجرة متعددة المستويات إذا أردنا عرض مثل هذه البنية متعددة المستويات، فنحن بحاجة إلى دالة متكررة.
<?php
// $parent هو والد الأطفال الذين نريد رؤيتهم
// يزداد مستوى $ عندما نتعمق في الشجرة،
// يُستخدم لعرض
دالة شجرة ذات مسافة بادئة لطيفة Display_children($parent, $level)
{
// احصل على جميع العقد الفرعية للعقدة الأصلية $parent
$result = mysql_query('اختر اسمًا من الشجرة'.
'WHEREparent="'.$parent.'";');
// عرض كل عقدة فرعية
بينما ($row = mysql_fetch_array($result))
{
// مسافة بادئة لاسم العقدة
echo str_repeat(' ',$level).$row['name']."n"
// اتصل بهذه الوظيفة مرة أخرى لعرض العقد الفرعية للعقدة الفرعية
Display_children($row['name'], $level+ 1)؛
}
}
?>
باستخدام هذه الوظيفة على العقدة الجذرية (الطعام) للبنية بأكملها، يمكن طباعة بنية الشجرة متعددة المستويات بأكملها، نظرًا لأن الغذاء هو العقدة الجذرية والعقدة الأصلية الخاصة به فارغة، اتصل: Display_children('',0). سيتم عرض محتويات الشجرة بأكملها:
طعام
الفاكهة
أحمر
الكرز
أصفر
موز
لحمة
لحم
لحم خنزير
إذا كنت تريد فقط عرض جزء من البنية بأكملها، مثل جزء الفاكهة، فيمكنك تسميتها على النحو التالي:
Display_children('Fruit',0);
باستخدام نفس الطريقة تقريبًا، يمكننا معرفة المسار من العقدة الجذرية إلى أي عقدة. على سبيل المثال، مسار الكرز هو "الطعام > الفاكهة > الأحمر". من أجل الحصول على مثل هذا المسار، نحتاج إلى البدء من المستوى الأعمق "Cherry"، والاستعلام للحصول على العقدة الأصلية "Red" وإضافتها إلى المسار، ثم نقوم بالاستعلام عن العقدة الأصلية لـ Red وإضافتها إلى المسار. ، وهكذا حتى المستوى الأعلى "الطعام"
<?php
// العقدة $ هي العقدة الأعمق
الدالة get_path(عقدة $)
{
// الاستعلام عن العقدة الأصلية لهذه العقدة
$result = mysql_query('اختر الأصل من الشجرة'.
'WHERE name="'.$node.'";');
$row = mysql_fetch_array($result);
// استخدم مصفوفة لحفظ المسار
$path = array();
// إذا لم تكن العقدة الجذرية، فاستمر في الاستعلام لأعلى
// (العقدة الجذرية لا تحتوي على عقدة أصل)
إذا ($الصف['الأصل']!='')
{
// الجزء الأخير من المسار إلى $node هو الاسم
// الأصل للعقدة $
$path[] = $row['parent'];
// يجب أن نضيف المسار إلى أصل هذه العقدة
// إلى المسار
$path = array_merge(get_path($row['parent']), $path);
}
// إرجاع المسار
إرجاع مسار $؛
}
?>
إذا كنت تستخدم هذه الوظيفة لـ "Cherry": print_r(get_path('Cherry'))، فستحصل على مصفوفة مثل هذا:
صفيف
(
[0] => طعام
[1] => فاكهة
[2] => أحمر
)
إن كيفية طباعتها بالتنسيق الذي تريده أمر متروك لك.
العيوب: هذه الطريقة بسيطة جدًا، وسهلة الفهم، وسهلة الاستخدام. ولكن هناك بعض العيوب. ويرجع ذلك أساسًا إلى أن سرعة التشغيل بطيئة جدًا، لأن كل عقدة تتطلب استعلام قاعدة بيانات، وعندما تكون كمية البيانات كبيرة، يلزم وجود العديد من الاستعلامات لإكمال الشجرة. بالإضافة إلى ذلك، نظرًا للحاجة إلى العمليات العودية، يجب أن يشغل كل مستوى من مستويات العودية بعض الذاكرة، وبالتالي فإن كفاءة استخدام المساحة منخفضة نسبيًا.
الآن دعونا نلقي نظرة على طريقة أخرى لا تستخدم الحسابات العودية وهي أسرع، وهي خوارزمية اجتياز شجرة الطلب المسبق المعدلة، وقد يكون تعرضك لهذه الطريقة أقل، وهي ليست مثل الطريقة المذكورة أعلاه عند استخدامها من السهل فهم الطريقة في المرة الأولى، ولكن نظرًا لأن هذه الطريقة لا تستخدم خوارزميات الاستعلام العودية، فإنها تتمتع بكفاءة استعلام أعلى.
نقوم أولاً برسم البيانات متعددة المستويات على الورق بالطريقة التالية، نكتب 1 على الجانب الأيسر من العقدة الجذرية Food، ثم نستمر أسفل الشجرة، ونكتب 2 على الجانب الأيسر من الفاكهة، ثم نواصل التحرك على طول الشجرة بأكملها قم بتسمية كل عقدة بالأرقام الموجودة على اليسار واليمين. الرقم الأخير هو 18 على يمين الطعام. في الصورة أدناه يمكنك رؤية الهيكل متعدد المستويات بالكامل والمميز بالأرقام. (ألا تفهم؟ أشر إلى الأرقام بأصابعك وعد من 1 إلى 18 وسوف تفهم. إذا كنت لا تزال لا تفهم، قم بالعد مرة أخرى واحرص على تحريك أصابعك).
تشير هذه الأرقام إلى العلاقة بين كل عقدة، أرقام "الأحمر" هي 3 و6، وهي العقد التابعة لـ "الغذاء" 1-18. وبالمثل، يمكننا أن نرى أن جميع العقد ذات القيمة اليسرى أكبر من 2 والقيمة اليمنى أقل من 11 هي من نسل "الفاكهة" 2-11
1 الغذاء 18
|
+---------------------------------------------+
|
2 الفاكهة 11 12 اللحوم 17
|
+----------------------+ +---------------------+
|
3 أحمر 6 7 أصفر 10 13 لحم بقري 14 15 لحم خنزير 16
|
4 الكرز 5 8 الموز 9
بهذه الطريقة، يمكن تخزين بنية الشجرة بأكملها في قاعدة البيانات من خلال القيم اليسرى واليمنى. قبل المتابعة، دعونا نلقي نظرة على جدول البيانات المجمعة أدناه.
+------------------------+-----+-----+
|.اسم |
+------------------------+-----+-----+
|
|.الفاكهة |
|.الفاكهة |
|.الكرز الأحمر |
|.الفاكهة |
|.الأصفر |
|.اللحوم |
|.لحم البقر |
|. لحم الخنزير |
+------------------------+-----+-----+
ملاحظة: بما أن "left" و"right" لهما معاني خاصة في SQL، فنحن بحاجة إلى استخدام "lft" و"rgt" لتمثيل الحقلين الأيسر والأيمن. بالإضافة إلى ذلك، لم تعد هناك حاجة إلى الحقل "الأصل" في هذه البنية لتمثيل بنية الشجرة. وبعبارة أخرى، بنية الجدول التالي كافية.
+-----------+-----+-----+
|
+-----------+-----+-----+
|
|
|
|.الكرز |.٤|
|
|
|.اللحوم ١٢ |
|
|
+-----------+-----+-----+
حسنًا، يمكننا الآن الحصول على البيانات من قاعدة البيانات، على سبيل المثال، إذا أردنا الحصول على جميع العقد ضمن عنصر "الفاكهة"، فيمكننا كتابة عبارة الاستعلام مثل هذا: SELECT * FROM Tree WHERE lft BETWEEN 2 AND 11; الاستعلام حصل على النتائج التالية .
+-----------+-----+-----+
|
+-----------+-----+-----+
|
|
|.الكرز |.٤|
|
|
+-----------+-----+-----+
كما ترى، يمكنك الحصول على كل هذه العقد باستعلام واحد فقط. لكي نتمكن من عرض بنية الشجرة بأكملها مثل الدالة العودية المذكورة أعلاه، نحتاج أيضًا إلى فرز مثل هذا الاستعلام. قم بالفرز حسب قيمة العقدة:
SELECT * FROM Tree WHERE lft BETWEEN 2 AND 11 ORDER BY lft ASC;
السؤال المتبقي هو كيفية عرض المسافة البادئة الهرمية.
<?php
الدالة Display_tree(الجذر $)
{
// احصل على القيم اليسرى واليمنى للعقدة الجذرية
$result = mysql_query('SELECT lft, rgt FROM Tree '.'WHERE name="'.$root.'";');
$row = mysql_fetch_array($result);
// تحضير مكدس rvalue فارغ
$right = array();
// الحصول على جميع العقد التابعة لنقطة الجذر
$result = mysql_query('حدد الاسم، lft، rgt من الشجرة'.
'أين lft بين '.$row['lft'].'
$row['rgt'].' ORDER BY lft ASC;');
// عرض كل صف
بينما ($row = mysql_fetch_array($result))
{
// تحقق فقط من المكدس إذا كان هناك واحد
إذا (العد($يمين)>0)
{
// تحقق مما إذا كان يجب علينا نقل العقدة خارج المكدس
بينما ($right[count($right)-1]<$row['rgt'])
{
array_pop($right);
}
}
// مسافة بادئة لاسم العقدة.
echo str_repeat(' ',count($right)).$row['name']."n";
// أضف هذه العقدة إلى المكدس
$right[] = $row['rgt'];
}
}
?>
إذا قمت بتشغيل الوظيفة المذكورة أعلاه، فستحصل على نفس نتيجة الوظيفة العودية. إن الأمر مجرد أن وظيفتنا الجديدة قد تكون أسرع نظرًا لوجود استعلامين فقط لقاعدة البيانات. من الأسهل معرفة مسار العقدة. إذا أردنا معرفة مسار Cherry، نستخدم قيمتيه اليمنى واليسرى 4 و5 لإجراء استعلام.
اختر الاسم من الشجرة حيث lft < 4 AND rgt > 5 ORDER BY lft ASC؛
سيعطيك هذا النتيجة التالية:
++----------------
|
+----------------+
|
|
|
+----------------+
إذن، كم عدد العقد التابعة التي تمتلكها عقدة معينة؟ الأمر بسيط جدًا، العدد الإجمالي للأحفاد = (قيمة r - القيمة اليسرى - 1)/2 أحفاد = (يمين - يسار - 1) / 2 ألا تصدق ذلك؟ قم بالحسابات بنفسك. باستخدام هذه الصيغة البسيطة، يمكننا أن نحسب بسرعة أن عقدة "Fruit 2-11" بها 4 عقد تنازلية، في حين أن عقدة "Banana 8-9" لا تحتوي على عقد تنازلية، مما يعني أنها ليست عقدة أصلية.
مذهل، أليس كذلك؟ على الرغم من أنني استخدمت هذه الطريقة عدة مرات، إلا أنها لا تزال تشعر بالروعة في كل مرة أفعلها.
هذه بالفعل طريقة جيدة، ولكن هل هناك أي طريقة لمساعدتنا في إنشاء جدول بيانات بقيم اليسار واليمين؟ إليك وظيفة أخرى لنقدمها لك. يمكن لهذه الوظيفة تحويل الجدول الذي يحتوي على الاسم والبنية الأصلية تلقائيًا إلى جدول بيانات بقيم يسارية ويمينية.
<?php
وظيفة إعادة بناء_شجرة($parent, $left) {
// القيمة اليمنى لهذه العقدة هي القيمة اليسرى + 1
$right = $left+1;
// الحصول على جميع أبناء هذه العقدة
$result = mysql_query('اختر اسمًا من الشجرة'.
'WHEREparent="'.$parent.'";');
بينما ($row = mysql_fetch_array($result)) {
// التنفيذ العودي لهذه الوظيفة لكل منها
// ابن هذه العقدة
// $right هي القيمة الصحيحة الحالية، وهي
// تتم زيادتها بواسطة دالة Rebuild_tree
$right = build_tree($row['name'], $right);
}
// لدينا القيمة اليسرى، والآن بعد أن قمنا بمعالجتها
// أطفال هذه العقدة نعرف أيضًا القيمة الصحيحة
mysql_query('مجموعة شجرة التحديث lft='.$left.', rgt='.
$right.' WHERE name = "'.$parent.'";');
// إرجاع القيمة الصحيحة لهذه العقدة + 1
إرجاع $right+1;
}
?>
بالطبع، هذه الوظيفة هي وظيفة عودية نحتاج إلى تشغيل هذه الوظيفة من العقدة الجذرية لإعادة بناء شجرة بالقيم اليمنى واليسرى
rebuild_tree('Food',1);
تبدو هذه الوظيفة معقدة بعض الشيء، لكن وظيفتها هي نفس وظيفة ترقيم الجدول يدويًا، وهي تحويل البنية ثلاثية الأبعاد متعددة الطبقات إلى جدول بيانات بقيم يسار ويمين.
فكيف يمكننا إضافة وتحديث وحذف عقدة لمثل هذا الهيكل؟ هناك عمومًا طريقتان لإضافة عقدة:
الاحتفاظ بالاسم الأصلي والبنية الأصلية، واستخدام الطريقة القديمة لإضافة البيانات إلى البيانات، واستخدام وظيفة Rebuild_tree لإعادة ترقيم البنية بأكملها بعد إضافة كل جزء من البيانات.
الطريقة الأكثر فعالية هي تغيير جميع القيم الموجودة على يمين العقدة الجديدة. على سبيل المثال: نريد إضافة فاكهة جديدة "فراولة" والتي ستصبح العقدة الفرعية الأخيرة للعقدة "الحمراء". أولا نحن بحاجة إلى توفير بعض المساحة لذلك. يجب تغيير القيمة اليمنى لـ "الأحمر" من 6 إلى 8، ويجب تغيير القيمة اليسرى واليمنى لـ "الأصفر 7-10" إلى 9-12. بالقياس، يمكننا أن نعرف أنه إذا كنت تريد إفساح المجال لقيم جديدة، فأنت بحاجة إلى إضافة 2 إلى جميع العقد ذات القيم اليسرى واليمنى أكبر من 5 (5 هي القيمة الصحيحة للعقدة الفرعية الأخيرة من "الأحمر" ). لذلك نقوم بإجراء عمليات قاعدة البيانات مثل هذا:
UPDATE Tree SET rgt=rgt+2 WHERE rgt>5;
تحديث مجموعة الشجرة lft=lft+2 WHERE lft>5;
يؤدي هذا إلى تحرير مساحة للقيمة المدرجة حديثًا. يمكنك الآن إنشاء عقدة بيانات جديدة في المساحة المحررة. قيمها اليسرى واليمنى هي 6 و7 على
التوالي "الفراولة"؛
لنجري استعلامًا آخر ونرى! ماذا عن ذلك؟ قريباً.
حسنًا، يمكنك الآن تصميم بنية قاعدة البيانات متعددة المستويات بطريقتين مختلفتين. الطريقة التي تستخدمها تعتمد كليًا على حكمك الشخصي، ولكن بالنسبة للهياكل ذات المستويات المتعددة والعدد الكبير، فإنني أفضل الطريقة الثانية. تكون الطريقة الأولى أسهل إذا كان حجم الاستعلام صغيرًا ولكن يجب إضافة البيانات وتحديثها بشكل متكرر.
بالإضافة إلى ذلك، إذا كانت قاعدة البيانات تدعمها، فيمكنك أيضًا كتابة إعادة البناء () وعملية تحرير المساحة كوظيفة تشغيل من جانب قاعدة البيانات، وتنفيذها تلقائيًا أثناء الإدراج والتحديث، وهذا يمكن أن يحقق كفاءة تشغيل أفضل إضافة العقد الجديدة ستصبح أسهل.
طريقة الصف العودية
مرسلة بواسطة ضيف في 2004، 31 مايو - 9:18 صباحًا.
لقد كتبت برنامجًا باستخدام طريقة شبه العودية، وهي ليست تمامًا نفس العودية المذكورة في المقالة، وأنا أستعد لزرعها في xoops:
http://dev.xoops.org/modules/xfmod/project/?ulink
واجه تجاوزًا في الذاكرة، ولكني أخطط لمواصلة استخدام الطريقة العودية، وأحتاج فقط إلى مواصلة التحسين،
وآمل أن تتاح لي الفرصة للمناقشة سم معك.
» الرد على هذا التعليق
أو المقارنة بين الطريقتين؟
مرسل بواسطة ضيف بتاريخ 2004، 17 مارس - 8:30 مساءً.
لقد درست هذه المقالة بعناية وشعرت أنها مفيدة للغاية، ولكن بعد ذلك فكرت فيها مرة أخرى وشعرت أن هناك مشكلة (من أجل الذاكرة، أسمي وضع الدليل المجاور الطريقة العودية، واجتياز الفرز المسبق خوارزمية الشجرة أسميها طريقة شجرة الفرز المسبق):
1. أكبر فرق بين الطريقتين هو أن التكرار يتطلب استخدام مكدس عند الاستعلام، بينما تتطلب شجرة الفرز المسبق نصف العقد (في إشارة إلى النصف الثاني من العقدة المدرجة) عند تحديث العقد من التحديثات. على الرغم من أنك قلت أيضًا أنه إذا كان هناك العديد من العقد والتحديثات المتكررة، فسيتم تقليل كفاءة الشجرة التي تم فرزها مسبقًا، وسيكون التكرار أفضل إذا كان هناك العديد من مستويات العقد، أولاً وقبل كل شيء، سيؤدي التكرار إلى تجاوز سعة المكدس، و علاوة على ذلك، فإن التكرار في حد ذاته ليس فعالاً للغاية، بالإضافة إلى أن كل مستوى من التكرار يتطلب تشغيل قاعدة البيانات، فإن التأثير الإجمالي لن يكون مثاليًا. نهجي الحالي هو إخراج جميع البيانات مرة واحدة، ثم إجراء عمليات متكررة على المصفوفة، والتي ستكون أفضل إذا كان من الممكن تحسينها بشكل أكبر، ويمكن إضافة عقدة جذر ROOT إلى كل صف من السجلات (حاليًا، فقط يتم تسجيل العقد الأصلية المجاورة) ، بحيث تكون الكفاءة عند البحث في الشجرة الفرعية أعلى، وستكون أيضًا مريحة جدًا عند تحديث الشجرة، والتي يجب أن تكون طريقة أفضل.
2. تحسين الطريقة العودية. في المقالة، عند حساب القيم اليسرى واليمنى لعقد الشجرة التي تم فرزها مسبقًا، يتم في الواقع استبدال المكدس بمصفوفة والدفع والبوب يتم تنفيذها يدويًا؛ إذا تمت الإشارة إلى هذه الطريقة في الخوارزمية العودية، وإذا كنت تستخدم مصفوفة بدلاً من المكدس عند تنفيذ العودية، فيمكنك أيضًا تحسين كفاءة العودية.
3. التزامن. إذا تم أخذ التزامن في الاعتبار، خاصة عند تحديث الشجرة، فإن طريقة تحديث معلومات العقدة في مساحة كبيرة من الشجرة التي تم فرزها مسبقًا تتطلب اهتمامًا إضافيًا باستخدام آليات القفل والمعاملات لضمان ذلك. اتساق البيانات.
4. في حالة العقد الجذرية المتعددة أو العقد الأصلية المتعددة، فمن الواضح أنها ليست شجرة ثنائية قياسية أو شجرة متعددة الشوكات تحتاج إلى تحسين كبير للتكيف والطريقة العودية يتم تطبيقه بحرية، لذلك في هذه الحالة، يكون التكرار أكثر قابلية للتكيف. هذا بالطبع، لأن الطريقة العودية هي شكل من أشكال القائمة المرتبطة، ويمكن التعبير عن الأشجار والرسوم البيانية من خلال القوائم المرتبطة، وبالطبع فهي قابلة للتكيف بدرجة كبيرة.
5. بديهية، إذا لاحظت مباشرة البيانات المخزنة في قاعدة البيانات دون تشغيل البرنامج، فمن الواضح أن البيانات المخزنة في الوضع العودي أكثر سهولة، في حين يصعب قراءة البيانات الموجودة في الشجرة التي تم فرزها مسبقًا مباشرة (للتسلسل الهرمي). العلاقات). وهذا أمر مهم في تبادل البيانات هل سيكون له أي تأثير؟
بشكل عام، أفضل شخصيًا استخدام الأساليب العودية، لكنني كنت دائمًا قلقًا بشأن تأثير التكرار على الكفاءة، ولحسن الحظ، لم أتعرض لمستويات تصنيف واسعة النطاق، وسيكون استخدام المصفوفات بشكل متكرر بدلاً من الأكوام بمثابة تحسين أفضل طريقة. تعد الشجرة التي تم فرزها مسبقًا طريقة فعالة لحل الأشجار البسيطة، بمجرد أن تعتاد عليها، يجب أن تكون جيدة جدًا، خاصة أن البحث العكسي من العقدة الورقية إلى العقدة الجذرية يكون مريحًا للغاية.
فولف
www.fwolf.com
» الرد على هذا التعليق
سعيد جدا لرؤية ردكم
مرسلة بواسطة shuke بتاريخ 2004، 18 مارس - 5:47 صباحًا.
أنا سعيد جدًا لأنك قرأت هذا المقال بعناية شديدة. تم نشر هذه المقالة في الأصل على موقع sitepoint.com، وقد قمت بترجمتها، على أمل تقديم بعض الأساليب للأصدقاء الذين يرغبون في البدء. طريقتك أيضًا جيدة جدًا، وسأحاول تجربتها إذا سنحت لي الفرصة. (إذا كنت مهتمًا، فلماذا لا تكتب أسلوبك ورمز التنفيذ المحدد كبرنامج تعليمي استنادًا إلى المثال أعلاه، حتى يتمكن الجميع من تقليده بمزيد من الأمثلة العملية) إذا كانت لديك أسئلة حول حفظ الهياكل متعددة المستويات في قاعدة البيانات إذا كنت إذا كنت مهتمًا بالبحث، إليك رابطين جيدين آخرين يمكن استخدامهما كمرجع:
تقديم الطرق الأربع الشائعة للاستعلام لمرة واحدة والبرنامج النصي لفرز المصفوفات، وأعتقد أن البرنامج النصي الخاص بك يجب أن يكون أفضل من هذا.
بالإضافة إلى ذلك، رأيت أنك تستخدم أيضًا drupal، كما أنه يحتوي على ميزة متقدمة تسمى نظام مصادقة المستخدم الموزع، طالما قمت بالتسجيل في أي موقع دروبال، يمكنك تسجيل الدخول للوصول إلى مواقع دروبال الأخرى. مثيرة للاهتمام للغاية.
أطيب التمنيات!
» الرد على هذا التعليق
تم تنفيذ بناء الأشجار باستخدام الحلقات
مرسل بواسطة ضيف بتاريخ 2004، 25 مارس - 10:10 مساءً.
لقد قرأت جميع المعلومات التي قدمتها في المرة السابقة، ولكن بصراحة، لا يوجد الكثير من الأشياء الجديدة في المقالة الأولى، ربما لم أفهمها جيدًا. المقالة الثانية مكتوبة بالفعل بلغة PHP3، وبنية البرنامج لم يتم تفصيله، انظر، يتم استخدام عدد كبير جدًا من التقاطعات الوظيفية.
لقد حدث أنني كنت بحاجة إلى استخدام أدوار المستخدم الهرمية في النظام، لذلك كتبت الاجتياز بناءً على فكرة المصفوفة، ولم يكن لدي الوقت لفرزها، لذلك سأضعها هنا لكي تلقي نظرة عليها، قاعدة البيانات هي ADODB، ويتم استخراج البرنامج مباشرة من النظام، وآمل أن يتم وصفه بوضوح. وهو يستخدم بشكل أساسي عمليات المصفوفة القوية الخاصة بـ PHP ويستخدم الحلقات لإجراء التكرار. التعليق طريقة مشابهة لكن توقيت معالجة النتائج مختلف.
<?php
/**
* عرض القائمة
* @الوصول العام
*/
وظيفة ديسبليست ()
{
// وضع العرض بدون مسافة بادئة
// $this->mIsDispListIndex = true;
// echo('<p align="right"><a href="?action=new&part=role">أضف دورًا جديدًا</a> </p>'); ">إضافة دور جديد</a> </p>');"
//
// $this->mListTitle = 'قائمة أدوار المستخدم';
// $this->SetDataOption('list');
//
// $this->SetQueryTable( array($this->mTableUserRole) );
//
// // ترتيب الاستعلام
// $this->SetQueryOrder( 'asc', $this->mTableUserRole, 'sequence' );
//
// $this->Query('list');
//parent::DispList();
// // طريقة عرض أخرى، تستخدم المصفوفة كمكدس، أ: احفظ الدور عند الضغط على المكدس، واحذف المصدر بعد الدفع.
// $this->CheckProperty('mrDb');
// $this->CheckProperty('mrSql');
// $this->mrSql->Select('role, title,parent');
// $this->mrSql->From($this->mTableUserRole);
// $this->mrSql->Orderby('parent, التسلسل');
// $this->mRs = $this->mrDb->Execute($this->mrSql->Sql());
// إذا (0 < العد($this->mRs))
// {
// $source = & $this->mRs->GetArray();
// $stack = array(''); //stack
// $stacki = array(-1); // يتوافق مع المكدس، ويسجل مستوى البيانات في المكدس في الشجرة
// $target = array();
// بينما (0 < العد($مكدس))
// {
// $item = array_shift($stack);
// $lev = array_shift($stacki);
// إذا (!فارغ($العنصر))
// {
// // ضع البيانات المعالجة في المصفوفة المستهدفة هنا
// array_push($target, str_repeat(' ', $lev) . $item);
// //$s1 = str_repeat(' ', $lev) .
// }
// $del = array(); // العقدة المراد حذفها من $source
// $ar = array(); // العقد التي يجب إضافتها إلى المكدس
// foreach ($المصدر كـ $key=>$val)
// {
// // ابحث عن العقد الفرعية المطابقة
// إذا (فارغة($العنصر))
// {
// $find = فارغ($source[$key]['parent']);
// }
//آخر
// {
// $find = ($item == $source[$key]['parent']);
// }
// إذا (بحث عن)
// {
// array_unshift($ar, $source[$key]['role']);
// $del[] = $key;
// }
// }
// foreach ($ar كـ $val)
// {
//array_unshift($stack, $val);
//array_unshift($stacki, $lev + 1);
// }
// foreach ($del كـ $val)
// {
// غير محدد($source[$val]);
// }
// echo(implode(', ', $stack) . '<br />' . implode(', ', $stacki) . '<br />' . implode(', ', $target) . '< br /><br />');
// }
//debug_array();
// }
//آخر
// {
// echo('<center>لم يتم استرداد البيانات</center>');
// }
// طريقة عرض أخرى، تستخدم المصفوفة كمكدس، ب: احفظ فهرس المصفوفة عند دفع المكدس، وأخرجه من المكدس، ثم احذف المصدر بعد الاستخدام.
$this->CheckProperty('mrDb');
$this->CheckProperty('mrSql');
$this->mrSql->Select('الدور، العنوان، الوالد');
$this->mrSql->From($this->mTableUserRole);
$this->mrSql->Orderby('parent, التسلسل');
$this->mRs = $this->mrDb->Execute($this->mrSql->Sql());
إذا (!فارغ($this->mRs) && !$this->mRs->EOF)
{
$source = & $this->mRs->GetArray();
$stack = array(-1);
$stacki = array(-1); // يتوافق مع المكدس، ويسجل مستوى البيانات في المكدس في الشجرة
$target = array();
بينما (0 < العد($مكدس))
{
$item = array_shift($stack);
$lev = array_shift($stacki);
إذا (-1!= عنصر $)
{
// ضع البيانات المعالجة في المصفوفة المستهدفة هنا
$s1 = str_repeat(' ', $lev) '<a href="?action=disp&part=role&role=' . $source[$item]['role'] . '">' $source[$item] . ['العنوان'] .
$s2 = '<a href="?action=edit&part=role&role=' . $source[$item]['role'] .'">تحرير</a> <a href="?action=delete&part=role&role= ' . $source[$item]['role'] .''>حذف</a>';
array_push($target, array($s1, $s2));
}
$del = array(); // العقدة المراد حذفها من $source
$ar = array(); // العقد التي يجب إضافتها إلى المكدس
foreach ($المصدر كـ $key=>$val)
{
// ابحث عن العقد الفرعية المطابقة
إذا (-1 == عنصر $)
{
$find = فارغ($source[$key]['parent']);
}
آخر
{
$find = ($source[$item]['role'] == $source[$key]['parent']);
}
إذا($العثور على)
{
array_unshift($ar, $key);
}
}
foreach ($ar كـ $val)
{
array_unshift($stack, $val);
array_unshift($stacki, $lev + 1);
}
//حذف من المصدر
غير محدد($source[$item]);
//echo(implode(', ', $stack) . '<br />' . implode(', ', $stacki) . '<br />' . implode(', ', $target) . '< br /><br />');
}
//الإخراج
echo('<p align="right"><a href="?action=new&part=role">أضف دورًا جديدًا</a> </p>');
array_unshift($target, array('role', 'operation'));
$this->CheckProperty('mrLt');
$this->mrLt->SetData($target);
$this->mrLt->mListTitle = 'قائمة أدوار المستخدم';
$this->mrLt->mIsDispIndex = false;
$this->mrLt->Disp();
}
آخر
{
echo('<center>لم يتم استرداد البيانات</center>');
}
} // نهاية الوظيفة DispList
?>