Utilisation des hiérarchies dans lsFusion

image

Dans diverses applications, il est souvent nécessaire d'implémenter une représentation hiérarchique des objets. Généralement, ceci est utilisé pour les classer en spécifiant des groupes. Ces groupes forment un arbre de profondeur dynamique, qui est ensuite utilisé pour la navigation, l'agrégation de données et le paramétrage.

Dans cet article, je montrerai comment cette logique peut être implémentée dans la plate-forme lsFusion ouverte et gratuite.

À titre d'exemple, prenons une logique simple dans laquelle vous devez implémenter la logique des produits regroupés en groupes spécifiques qui forment une hiérarchie de profondeur dynamique. Dans ce cas, les marchandises peuvent être liées à un nœud intermédiaire de l'arbre.

Tout d'abord, selon le schéma standard, déclarez l'entité Groupe de produits comme une simple classe plate avec des formulaires d'édition et une liste:
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;
}

Faisons maintenant une hiérarchie à partir des groupes. Pour ce faire, nous introduisons une propriété qui contiendra un lien vers le groupe parent:
parent = DATA Group (Group);
nameParent ' ' (Group g) = name(parent(g));

Ensuite, nous créons une propriété qui déterminera récursivement la relation entre deux groupes:
level '' (Group child, Group parent) =
RECURSION 1l IF child IS Group AND parent = child
STEP 2l IF parent = parent($parent) MATERIALIZED ;

Sur quel principe fonctionne l'opérateur RECURSION , je ne décrirai pas dans cet article, mais la propriété level renverra 2 au degré «longueur du chemin entre l' enfant et le parent dans l'arbre directionnel correspondant». MATERIALIZED indique que la plate-forme doit le stocker dans une table séparée, où pour chaque paire de nœuds connectés, il y aura un enregistrement séparé avec la valeur de niveau dans la colonne correspondante. Avec tout changement dans la structure de l'arborescence, ce tableau sera automatiquement recompté.

Par exemple, pour un tel arbre:

image

Le tableau ressemblera à ceci:

image

Dans ce document, key0 est le code descendant et key1 est le code parent. Le nombre d'entrées dans ce tableau sera approximativement égal au nombre de groupes multiplié par la profondeur moyenne de l'arbre. Un tel schéma de stockage sera utile dans la mesure où si vous devez compter tous les descendants du groupe, vous n'avez pas à recourir aux requêtes CTE, mais vous pouvez utiliser le JOIN habituel pour ce tableau.

De plus, en fonction de la propriété construite, le nom canonique du groupe peut être calculé:
canonicalName ' ' (Group group) =
GROUP CONCAT name(Group parent), ' / ' ORDER DESC level(group, parent) CHARWIDTH 50 ;

Par exemple, pour le groupe Milk dans l'image ci-dessus, le nom canonique serait All / Groceries / Dairy / Milk . CHARWIDTH est spécifié afin d'indiquer à la plateforme la largeur à utiliser pour cette propriété (en caractères) lors de la construction de l'interface.

Nous allons maintenant développer le formulaire pour afficher et modifier des groupes avec des propriétés nouvellement créées:
EXTEND FORM group
PROPERTIES (g) nameParent, canonicalName
;

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

Un formulaire avec une liste de groupes sous forme plate ressemblera à ceci:

image

Une fois la logique des groupes terminée, ajoutez l'entité Product :
CLASS Product '' ;
name '' = DATA ISTRING [ 50 ] (Product);

Créez un lien de produit vers le groupe de produits auquel il appartient:
group '' = DATA Group (Product);
canonicalNameGroup ' ' (Product p) = canonicalName(group(p));

Enfin, nous allons créer un formulaire de saisie de marchandises, dans lequel il y aura deux éléments: un arbre de groupes et une liste de marchandises. Pour le groupe d'arborescence sélectionné, seuls les produits appartenant à un descendant du nœud sélectionné seront affichés dans la liste. Tout d'abord, déclarez un formulaire et ajoutez-y une arborescence avec une liste de groupes:
FORM products ''
TREE groups g = Group PARENT parent
PROPERTIES READONLY name(g)
;

À l'aide de la commande TREE , une arborescence est créée à partir d'objets de la classe Group , dont la hiérarchie est déterminée par la propriété parent précédemment créée.

Ajoutez le formulaire au navigateur:
NAVIGATOR {
NEW products;
}

Dans cet exemple, la saisie et la modification des marchandises seront effectuées non pas via des boîtes de dialogue distinctes, mais directement dans le formulaire lui-même. Pour ce faire, créez une action pour créer un produit en référence au groupe sélectionné:
newProduct '' (Group g) {
NEW p = Product {
group(p) <- g;
}
}

Maintenant, sur le formulaire créé précédemment, ajoutez la liste des produits avec des colonnes modifiables:
EXTEND FORM products
OBJECTS p = Product
PROPERTIES (p) name, canonicalNameGroup
FILTERS level(group(p), g)
;

Jetez sur les boutons du formulaire pour ajouter et supprimer des marchandises:
EXTEND FORM products
PROPERTIES newProduct(g) DRAW p TOOLBAR , DELETE (p)
;

Étant donné que l'action newProduct est définie pour un groupe de produits, il doit être spécifié explicitement qu'elle doit être ajoutée à la barre d'outils avec la liste des produits (p).

Il reste à configurer le design pour que l'arborescence soit affichée à gauche, et la liste des produits à droite, et entre eux il y a un séparateur, avec lequel vous pouvez redimensionner les objets:
DESIGN products {
OBJECTS {
NEW pane {
type = SPLITH ;
fill = 1 ;
MOVE BOX ( TREE groups);
MOVE BOX (p);
}
}
}

La forme finale ressemblera à ceci:

image

Une fois la hiérarchie des produits et des groupes créée, il est souvent nécessaire de définir un paramètre à l'un des niveaux. De plus, plus la hiérarchie est spécifiée, plus sa valeur est élevée. Par exemple, si le groupe Produits laitiers est défini sur 30 et le groupe Lait est défini sur 20, le dernier doit être sélectionné.

Supposons que vous souhaitiez définir l'option premium de cette manière. Pour ce faire, créez d'abord la propriété appropriée pour le groupe:
markup ', %' = DATA NUMERIC [ 10 , 2 ] (Group);

Afin de trouver la valeur souhaitée, il suffit d'utiliser le groupement avec le choix de la dernière valeur:
parentMarkup ' ( ), %' (Group child) =
GROUP LAST markup(Group parent) ORDER DESC level(child, parent) WHERE markup(parent);

Traduite en langage ordinaire, cette expression trouve ( GROUP ) le dernier ( LAST ) balisage du groupe supérieur ( Group parent ), par ordre décroissant de distance ( ORDER DESC level (enfant, parent) ), pour lequel ce balisage donné ( WHERE markup (parent) ). Ici, je veux noter comment lsFusion correspond au langage naturel.

Ajoutez les propriétés créées ci-dessus au formulaire de produit dans l'arborescence de groupe:
EXTEND FORM products
PROPERTIES (g) markup, parentMarkup READONLY
;

Supposons qu'il soit nécessaire de fixer une prime directement pour un produit et qu'elle devrait être supérieure à la prime pour le groupe. Pour ce faire, créez d'abord la propriété principale du produit:
dataMarkup ' , %' = DATA NUMERIC [ 10 , 2 ] (Product);

Ensuite, nous déclarons une propriété qui remboursera la prime du produit, le cas échéant, ou la prime du groupe:
markup ', %' (Product p) = OVERRIDE dataMarkup(p), parentMarkup(group(p));

Après cela, ajoutez les deux propriétés au formulaire:
EXTEND FORM products
PROPERTIES (p) dataMarkup, markup READONLY
;

Le mécanisme de fixation des primes pour les groupes et les produits ressemblera à ceci:

image

Conclusion


Dans l'article ci-dessus, nous avons pu créer la logique des marchandises, les combiner en groupes avec une hiérarchie de profondeur dynamique et également fournir à l'utilisateur la possibilité de fixer des primes à n'importe quel niveau. Tout cela a pris environ 70 lignes de code importantes. Vous pouvez essayer son fonctionnement en ligne et apporter vos modifications au code dans la section appropriée du site (onglet Plate-forme). Voici le code source complet que vous devez coller dans le champ approprié:

Code source
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
;

Le modèle décrit ci-dessus peut être modifié et utilisé de diverses manières en ajoutant des paramètres supplémentaires aux propriétés. Par exemple, dans une mise en œuvre du système ERP, les primes pour les groupes et les biens sont fixées de cette manière non pas globalement, mais séparément pour chaque type de prix. De plus, la mise en œuvre en complexité n'est pas différente de l'exemple décrit ci-dessus.

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


All Articles