العمل مع التسلسلات الهرمية في lsFusion

صورة

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

في هذه المقالة ، سأظهر كيف يمكن تنفيذ هذا المنطق في النظام الأساسي المفتوح والمجاني lsFusion .

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

أولاً ، وفقًا للمخطط القياسي ، قم بتعريف كيان مجموعة المنتج كفئة مسطحة بسيطة مع تحرير النماذج وقائمة:
CLASS Group '' ;
name '' = DATA ISTRING [ 50 ] (Group);

FORM group ''
OBJECTS g = Group PANEL
PROPERTIES (g) name

EDIT Group OBJECT g
;

FORM groups ''
OBJECTS g = Group
PROPERTIES (g) READONLY name
PROPERTIES (g) NEWSESSION NEW , EDIT , DELETE

LIST Group OBJECT g
;

NAVIGATOR {
NEW groups;
}

الآن لنجعل التسلسل الهرمي خارج المجموعات. للقيام بذلك ، نقدم خاصية تحتوي على رابط للمجموعة الأصل:
parent = DATA Group (Group);
nameParent ' ' (Group g) = name(parent(g));

بعد ذلك ، نجعل خاصية ستحدد العلاقة بين المجموعتين بشكل متكرر:
level '' (Group child, Group parent) =
RECURSION 1l IF child IS Group AND parent = child
STEP 2l IF parent = parent($parent) MATERIALIZED ;

على أي مبدأ يعمل مشغل RECURSION ، لن أصف في هذه المقالة ، لكن خاصية المستوى ستُرجع 2 إلى درجة "طول المسار بين الطفل والوالد في شجرة الاتجاه المقابلة". يشير MATERIALIZED إلى أن النظام الأساسي يجب أن يخزنه في جدول منفصل ، حيث سيكون لكل سجل من العقد المتصلة سجل منفصل مع قيمة المستوى في العمود المقابل. مع أي تغيير في بنية الشجرة ، سيتم إعادة حساب هذا الجدول تلقائيًا.

على سبيل المثال ، لمثل هذه الشجرة:

صورة

سيبدو الجدول كالتالي:

صورة

في ذلك ، key0 هو الكود التنازلي ، و key1 هو الكود الأصلي. سيكون عدد الإدخالات في هذا الجدول مساويًا تقريبًا لعدد المجموعات التي يبلغ متوسط ​​عمق الشجرة فيها. سيكون نظام التخزين هذا مفيدًا إذا كنت بحاجة إلى حساب جميع أحفاد المجموعة ، فلن تضطر إلى اللجوء إلى طلبات CTE ، ولكن يمكنك استخدام JOIN المعتاد لهذا الجدول.

علاوة على ذلك ، بناءً على الخاصية التي تم إنشاؤها ، يمكن حساب الاسم الأساسي للمجموعة:
canonicalName ' ' (Group group) =
GROUP CONCAT name(Group parent), ' / ' ORDER DESC level(group, parent) CHARWIDTH 50 ;

على سبيل المثال ، بالنسبة لمجموعة Milk في الصورة أعلاه ، سيكون الاسم الأساسي هو All / Groceries / Dairy / Milk . يتم تحديد CHARWIDTH لإخبار النظام الأساسي عن العرض المطلوب استخدامه لهذه الخاصية (بالأحرف) عند إنشاء الواجهة.

الآن سنقوم بتوسيع النموذج لعرض وتحرير المجموعات مع الخصائص التي تم إنشاؤها حديثا:
EXTEND FORM group
PROPERTIES (g) nameParent, canonicalName
;

EXTEND FORM groups
PROPERTIES (g) READONLY nameParent, canonicalName
;

سيبدو النموذج مع قائمة المجموعات في نموذج مسطح كما يلي:

صورة

بعد اكتمال منطق المجموعات ، أضف منتج الكيان:
CLASS Product '' ;
name '' = DATA ISTRING [ 50 ] (Product);

قم بإنشاء رابط منتج لمجموعة المنتجات التي ينتمي إليها:
group '' = DATA Group (Product);
canonicalNameGroup ' ' (Product p) = canonicalName(group(p));

أخيرًا ، سنقوم بإنشاء نموذج لإدخال البضائع ، حيث سيكون هناك عنصرين: شجرة المجموعات وقائمة البضائع. بالنسبة للمجموعة الشجرية المحددة ، سيتم عرض المنتجات التي تنتمي إلى أي سليل للعقدة المحددة فقط في القائمة. أولاً ، قم بتعريف نموذج وإضافة شجرة إليه مع قائمة بالمجموعات:
FORM products ''
TREE groups g = Group PARENT parent
PROPERTIES READONLY name(g)
;

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

أضف النموذج إلى المستكشف:
NAVIGATOR {
NEW products;
}

في هذا المثال ، لن يتم إدخال البضائع وتحريرها من خلال مربعات حوار منفصلة ، ولكن مباشرةً في النموذج نفسه. للقيام بذلك ، قم بإنشاء إجراء لإنشاء منتج مع الإشارة إلى المجموعة المحددة:
newProduct '' (Group g) {
NEW p = Product {
group(p) <- g;
}
}

الآن ، في النموذج الذي تم إنشاؤه مسبقًا ، أضف قائمة المنتجات ذات الأعمدة القابلة للتعديل:
EXTEND FORM products
OBJECTS p = Product
PROPERTIES (p) name, canonicalNameGroup
FILTERS level(group(p), g)
;

رمي على أزرار النموذج لإضافة وإزالة البضائع:
EXTEND FORM products
PROPERTIES newProduct(g) DRAW p TOOLBAR , DELETE (p)
;

نظرًا لأن الإجراء newProduct محدد لمجموعة من المنتجات ، فيجب الإشارة بوضوح إلى أنه يجب إضافته إلى شريط الأدوات مع قائمة المنتجات (ع).

يبقى تكوين التصميم بحيث يتم عرض الشجرة على اليسار ، وقائمة المنتجات على اليمين ، وهناك فاصل بينهما ، يمكنك تغيير حجم الكائنات به:
DESIGN products {
OBJECTS {
NEW pane {
type = SPLITH ;
fill = 1 ;
MOVE BOX ( TREE groups);
MOVE BOX (p);
}
}
}

الشكل النهائي سيبدو كما يلي:

صورة

بعد إنشاء التسلسل الهرمي للمنتجات والمجموعات ، غالبًا ما تكون هناك حاجة لتعيين بعض المعلمات في أي من المستويات. علاوة على ذلك ، كلما تم تحديد التسلسل الهرمي ، زادت قيمته. على سبيل المثال ، إذا تم تعيين Dairy group على 30 ، وتم تعيين Milk Milk على 20 ، فيجب تحديد المجموعة الأخيرة.

لنفترض أنك تريد تحديد الخيار المتميز بهذه الطريقة. للقيام بذلك ، قم أولاً بإنشاء الخاصية المناسبة للمجموعة:
markup ', %' = DATA NUMERIC [ 10 , 2 ] (Group);

من أجل العثور على القيمة المطلوبة ، ما عليك سوى استخدام المجموعة مع اختيار القيمة الأخيرة:
parentMarkup ' ( ), %' (Group child) =
GROUP LAST markup(Group parent) ORDER DESC level(child, parent) WHERE markup(parent);

يترجم هذا التعبير إلى لغة عادية ، ويجد ( GROUP ) آخر علامة ( LAST ) في المجموعة العليا ( المجموعة الأم ) ، بترتيب تنازلي للمسافة التي تصل إليها ( ORDER DESC level ( التابع ، الوالد) ) ، وهذا الترميز له معين ( أين العلامات (الوالد) ). هنا أود أن أشير إلى كيفية توافق lsFusion مع اللغة الطبيعية.

أضف الخصائص التي تم إنشاؤها أعلاه إلى نموذج المنتج في شجرة المجموعة:
EXTEND FORM products
PROPERTIES (g) markup, parentMarkup READONLY
;

افترض أن هناك حاجة لتعيين علاوة مباشرة للمنتج ، وأنه يجب أن يكون أعلى من علاوة المجموعة. للقيام بذلك ، قم أولاً بإنشاء الخاصية الأساسية للمنتج:
dataMarkup ' , %' = DATA NUMERIC [ 10 , 2 ] (Product);

ثم نعلن عن خاصية تعيد القسط من المنتج ، إذا تم تحديده ، أو القسط من المجموعة:
markup ', %' (Product p) = OVERRIDE dataMarkup(p), parentMarkup(group(p));

بعد ذلك ، قم بإضافة كلا الخصائص إلى النموذج:
EXTEND FORM products
PROPERTIES (p) dataMarkup, markup READONLY
;

ستبدو آلية تحديد العلاوات للمجموعات والمنتجات كما يلي:

صورة

استنتاج


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

شفرة المصدر
CLASS Group '' ;
name '' = DATA ISTRING [ 50 ] (Group);

FORM group ''
OBJECTS g = Group PANEL
PROPERTIES (g) name

EDIT Group OBJECT g
;

FORM groups ''
OBJECTS g = Group
PROPERTIES (g) READONLY name
PROPERTIES (g) NEWSESSION NEW , EDIT , DELETE

LIST Group OBJECT g
;

NAVIGATOR {
NEW groups;
}

parent = DATA Group (Group);
nameParent ' ' (Group g) = name(parent(g));

level '' (Group child, Group parent) =
RECURSION 1l IF child IS Group AND parent = child
STEP 2l IF parent = parent($parent) MATERIALIZED ;

canonicalName ' ' (Group group) =
GROUP CONCAT name(Group parent), ' / ' ORDER DESC level(group, parent) CHARWIDTH 50 ;

EXTEND FORM group
PROPERTIES (g) nameParent, canonicalName
;

EXTEND FORM groups
PROPERTIES (g) READONLY nameParent, canonicalName
;

CLASS Product '' ;
name '' = DATA ISTRING [ 50 ] (Product);

group '' = DATA Group (Product);
canonicalNameGroup ' ' (Product p) = canonicalName(group(p));

FORM products ''
TREE groups g = Group PARENT parent
PROPERTIES READONLY name(g)
;

NAVIGATOR {
NEW products;
}

newProduct '' (Group g) {
NEW p = Product {
group(p) <- g;
}
}
EXTEND FORM products
OBJECTS p = Product
PROPERTIES (p) name, canonicalNameGroup
FILTERS level(group(p), g)
;

EXTEND FORM products
PROPERTIES newProduct(g) DRAW p TOOLBAR , DELETE (p)
;

DESIGN products {
OBJECTS {
NEW pane {
type = SPLITH ;
fill = 1 ;
MOVE BOX ( TREE groups);
MOVE BOX (p);
}
}
}

markup ', %' = DATA NUMERIC [ 10 , 2 ] (Group);

parentMarkup ' ( ), %' (Group child) =
GROUP LAST markup(Group parent) ORDER DESC level(child, parent) WHERE markup(parent);

EXTEND FORM products
PROPERTIES (g) markup, parentMarkup READONLY
;

dataMarkup ' , %' = DATA NUMERIC [ 10 , 2 ] (Product);
markup ', %' (Product p) = OVERRIDE dataMarkup(p), parentMarkup(group(p));

EXTEND FORM products
PROPERTIES (p) dataMarkup, markup READONLY
;

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

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


All Articles