Bekerja dengan hierarki di lsFusion

gambar

Dalam berbagai aplikasi, sering kali ada kebutuhan untuk mengimplementasikan representasi objek secara hierarkis. Biasanya, ini digunakan untuk mengklasifikasikan mereka dengan menentukan grup. Grup-grup ini membentuk pohon kedalaman dinamis, yang kemudian digunakan untuk navigasi, agregasi data, dan pengaturan parameter.

Pada artikel ini, saya akan menunjukkan bagaimana logika ini dapat diimplementasikan di platform lsFusion terbuka dan gratis.

Sebagai contoh, mari kita ambil logika sederhana di mana Anda perlu mengimplementasikan logika produk yang dikelompokkan ke dalam kelompok tertentu yang membentuk hierarki kedalaman dinamis. Dalam hal ini, barang dapat diikat ke simpul menengah dari pohon.

Pertama, sesuai dengan skema standar, nyatakan entitas grup Produk sebagai kelas rata sederhana dengan formulir pengeditan dan daftar:
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;
}

Sekarang mari kita membuat hierarki dari grup. Untuk melakukan ini, kami memperkenalkan properti yang akan berisi tautan ke grup induk:
parent = DATA Group (Group);
nameParent ' ' (Group g) = name(parent(g));

Selanjutnya, kami membuat properti yang secara rekursif akan menentukan hubungan antara dua grup:
level '' (Group child, Group parent) =
RECURSION 1l IF child IS Group AND parent = child
STEP 2l IF parent = parent($parent) MATERIALIZED ;

Pada prinsip apa operator RECURSION bekerja , saya tidak akan menjelaskan dalam artikel ini, tetapi properti level akan mengembalikan 2 ke tingkat "panjang jalur antara anak dan orang tua di pohon directional yang sesuai". MATERIALISASI menunjukkan bahwa platform harus menyimpannya dalam tabel terpisah, di mana untuk setiap pasangan node yang terhubung akan ada catatan terpisah dengan nilai level di kolom terkait. Dengan perubahan apa pun dalam struktur pohon, tabel ini akan secara otomatis dihitung ulang.

Misalnya, untuk pohon seperti itu:

gambar

Tabelnya akan terlihat seperti ini:

gambar

Di dalamnya, key0 adalah kode turunan, dan key1 adalah kode induk. Jumlah entri dalam tabel ini kira-kira sama dengan jumlah grup dikalikan kedalaman rata-rata pohon. Skema penyimpanan seperti itu akan berguna karena jika Anda perlu menghitung semua keturunan grup, Anda tidak perlu menggunakan permintaan CTE, tetapi Anda dapat menggunakan GABUNGAN biasa untuk tabel ini.

Selanjutnya, berdasarkan pada properti yang dibangun, nama kanonik grup dapat dihitung:
canonicalName ' ' (Group group) =
GROUP CONCAT name(Group parent), ' / ' ORDER DESC level(group, parent) CHARWIDTH 50 ;

Sebagai contoh, untuk kelompok Susu pada gambar di atas, nama kanonik adalah Semua / Bahan Makanan / Susu / Susu . CHARWIDTH ditentukan untuk memberi tahu platform berapa lebar yang digunakan untuk properti ini (dalam karakter) saat membangun antarmuka.

Sekarang kita akan memperluas formulir untuk melihat dan mengedit grup dengan properti yang baru dibuat:
EXTEND FORM group
PROPERTIES (g) nameParent, canonicalName
;

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

Formulir dengan daftar grup dalam bentuk datar akan terlihat seperti ini:

gambar

Setelah logika grup selesai, tambahkan entitas Produk :
CLASS Product '' ;
name '' = DATA ISTRING [ 50 ] (Product);

Buat tautan produk ke grup produk tempat ia berada:
group '' = DATA Group (Product);
canonicalNameGroup ' ' (Product p) = canonicalName(group(p));

Akhirnya, kita akan membuat formulir untuk memasukkan barang, di mana akan ada dua elemen: pohon kelompok dan daftar barang. Untuk grup pohon yang dipilih, hanya produk yang merupakan turunan dari simpul yang dipilih yang akan ditampilkan dalam daftar. Pertama, deklarasikan formulir dan tambahkan pohon ke dalamnya dengan daftar grup:
FORM products ''
TREE groups g = Group PARENT parent
PROPERTIES READONLY name(g)
;

Menggunakan perintah TREE , pohon dibuat dari objek kelas Grup , hierarki yang ditentukan oleh properti induk yang dibuat sebelumnya.

Tambahkan formulir ke navigator:
NAVIGATOR {
NEW products;
}

Dalam contoh ini, input dan pengeditan barang akan dilakukan tidak melalui dialog terpisah, tetapi langsung dalam bentuk itu sendiri. Untuk melakukan ini, buat tindakan untuk membuat produk dengan referensi ke grup yang dipilih:
newProduct '' (Group g) {
NEW p = Product {
group(p) <- g;
}
}

Sekarang, pada formulir yang dibuat sebelumnya, tambahkan daftar produk dengan kolom yang dapat diedit:
EXTEND FORM products
OBJECTS p = Product
PROPERTIES (p) name, canonicalNameGroup
FILTERS level(group(p), g)
;

Lempar tombol form untuk menambah dan menghapus barang:
EXTEND FORM products
PROPERTIES newProduct(g) DRAW p TOOLBAR , DELETE (p)
;

Karena tindakan newProduct didefinisikan untuk grup produk, itu harus ditentukan secara eksplisit bahwa itu harus ditambahkan ke toolbar dengan daftar produk (p).

Masih mengkonfigurasi desain sehingga pohon ditampilkan di sebelah kiri, dan daftar produk di sebelah kanan, dan di antara mereka ada pemisah, yang dengannya Anda dapat mengubah ukuran objek:
DESIGN products {
OBJECTS {
NEW pane {
type = SPLITH ;
fill = 1 ;
MOVE BOX ( TREE groups);
MOVE BOX (p);
}
}
}

Bentuk akhir akan terlihat seperti ini:

gambar

Setelah hierarki produk dan grup dibuat, sering kali ada kebutuhan untuk mengatur beberapa parameter di salah satu level. Selain itu, semakin rendah hierarki yang ditentukan, semakin tinggi nilainya. Misalnya, jika grup Dairy diatur ke 30, dan grup Milk diatur ke 20, maka yang terakhir harus dipilih.

Misalkan Anda ingin mendefinisikan opsi premium dengan cara ini. Untuk melakukan ini, pertama buat properti yang sesuai untuk grup:
markup ', %' = DATA NUMERIC [ 10 , 2 ] (Group);

Untuk menemukan nilai yang diinginkan, cukup gunakan pengelompokan dengan pilihan nilai terakhir:
parentMarkup ' ( ), %' (Group child) =
GROUP LAST markup(Group parent) ORDER DESC level(child, parent) WHERE markup(parent);

Diterjemahkan ke dalam bahasa biasa, ungkapan ini menemukan ( GROUP ) markup terakhir ( TERAKHIR ) di grup atas ( induk Grup ), dalam mengurangi urutan jarak ke sana ( level ORDER DESC (anak, orangtua) ), yang untuk markup ini diberikan ( marka WHERE (orang tua) ). Di sini saya ingin mencatat bagaimana lsFusion sesuai dengan bahasa alami.

Tambahkan properti yang dibuat di atas ke bentuk produk di pohon grup:
EXTEND FORM products
PROPERTIES (g) markup, parentMarkup READONLY
;

Misalkan ada kebutuhan untuk menetapkan premi langsung untuk suatu produk, dan itu harus lebih tinggi daripada premi untuk grup. Untuk melakukan ini, pertama buat properti utama untuk produk:
dataMarkup ' , %' = DATA NUMERIC [ 10 , 2 ] (Product);

Kemudian kami mendeklarasikan properti yang akan mengembalikan premi dari produk, jika ditentukan, atau premi dari grup:
markup ', %' (Product p) = OVERRIDE dataMarkup(p), parentMarkup(group(p));

Setelah itu, tambahkan kedua properti ke formulir:
EXTEND FORM products
PROPERTIES (p) dataMarkup, markup READONLY
;

Mekanisme untuk menetapkan premi untuk grup dan produk akan terlihat seperti ini:

gambar

Kesimpulan


Pada artikel di atas, kami dapat membuat logika barang, menggabungkannya ke dalam kelompok dengan hierarki kedalaman dinamis, dan juga memberikan pengguna kemampuan untuk menetapkan premi di tingkat mana pun. Semua ini membutuhkan sekitar 70 baris kode yang signifikan. Anda dapat mencoba cara kerjanya secara online, serta membuat perubahan pada kode di bagian situs yang sesuai (tab Platform). Berikut ini seluruh kode sumber yang harus Anda tempel ke bidang yang sesuai:

Kode sumber
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
;

Templat yang dijelaskan di atas dapat dimodifikasi dan digunakan dengan berbagai cara dengan menambahkan parameter tambahan ke properti. Misalnya, dalam satu implementasi sistem ERP, premi untuk grup dan barang ditetapkan dengan cara ini tidak secara global, tetapi secara terpisah untuk setiap jenis harga. Selain itu, implementasi dalam kompleksitas tidak berbeda dengan contoh yang dijelaskan di atas.

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


All Articles