V počítačovém světě je často užitečné nebo dokonce nezbytné, aby se databáze pružně přizpůsobovaly uživatelům a jejich podnikání. Opakovaným a citlivým tématem je také kontrola přístupných dat. Z tohoto hlediska vývojáři používají metody a vzorce, které jsou někdy složité, aby umožnily nebo omezily přístup k informacím v závislosti na kontextu nebo přístupových právech uživatelů.
Uveďme si jednoduchý příklad. Ve své aplikaci někdy potřebujete zobrazit seznam osob. V jednom ze sloupců se zobrazují jejich celá jména, ale v databázi máte pole pro jméno a pole pro příjmení. V současné době píšete vzorec ve sloupci pole seznamu a třídění ve sloupci si musíte řídit sami. Nebylo by skvělé mít vypočtené pole, kde byste mohli definovat jeho vzorec výpočtu a metodu řazení a mít veškerou obchodní logiku uvnitř třídy a ne v každém rozhraní?
No, počínaje verzí 4D v19 R3 nabízí 4D řešení tohoto problému, a to pomocí vypočtených atributů.
PŘEDSTAVTE SI…
Máte seznam osob, jejichž příjmení, jména, data narození atd.
Představte si, že chcete ve formuláři zobrazit seznam osob s jejich celým jménem (na základě jména + příjmení), věkem (na základě data narození), aktuální fotografií (na základě věku)….
Jakmile je pak osoba vybrána, můžete zobrazit informace o jejích rodičích, prarodičích, dětech, sourozencích, strýcích a tetách a nezapomeňme na bratrance a sestřenice!
Je to možné? Ano, samozřejmě.
Bez složitých řádků kódu ve formuláři? Ano!
Všechna tato kouzla jsou možná díky vypočteným atributům, na kterých je možné také vyhledávat a třídit, stejně jako u ostatních atributů! Pojďme se na to podívat blíže!
Definice a výpočty
Abychom vysvětlili sílu tohoto konceptu, začneme konkrétním příkladem. Datová třída „lidé“ klasicky obsahuje atributy jako „příjmení“, „jméno“, „adresa“, „PSČ“, „město“, „země“, „datum narození“ atd.
Z této třídy můžeme potřebovat celé jméno (příjmení + jméno), věk (na základě data narození) nebo celou adresu (jako objekt).
A můžeme jít i dále. Pokud to vztahy umožňují, mohou být vypočtené atributy dokonce typu entity (např. otec nebo matka) nebo výběru entit (děti, rodiče, prarodiče, sourozenci atd.).
Přístup k těmto atributům bude umožněn prostřednictvím třídy „peopleEntity“, ve které je třeba za tímto účelem definovat nové funkce.
Budou volány, když 4D bude potřebovat přístup k těmto atributům, a to buď pro jejich čtení (např. prosté zobrazení), při vyhledávání nebo třídění a konečně při případném ukládání po úpravě.
Výsledek vypočtených atributů, např. úplná adresa, může potřebovat další vypočtený atribut, např. úplné jméno. I tento typ rekurze je možný; k tomu se ještě vrátíme.
Tyto functions (get, set a také query a orderBy, o kterých bude řeč později) budou definovány uvnitř samotné třídy entit (např. peopleEntity).
Přístup k vypočteným atributům
Získat
První funkce, „get“, umožňuje definovat způsob výpočtu atributu. Za tímto účelem musí vrátit výsledek výpočtu.
Tato funkce je jediná, která je povinná pro použití vypočtených atributů. Ve skutečnosti pouze její přítomnost určuje samotnou existenci tohoto atributu.
Function get fullName($event : Object) -> $result : Text
If (This.firstname=Null)
$result :=This.lastname
Else
If (This.lastname=Null)
$result :=This.firstname
Else
$result :=This.firstname+" "+This.lastname
End if
End if
Vystavený, místní … nebo ne?
Jak bude tento atribut přístupný a za jakých podmínek se bude počítat?
Klíčové slovo „exposed“, které lze použít k definici funkce, je ekvivalentem zaškrtávacího políčka „Exposed“ v inspektoru editoru struktury.
Klíčové slovo „local“ použité v režimu klient-server určuje, zda se má výpočet provádět systematicky na serveru, nebo zda se má provádět lokálně, aby se omezil přístup k síti.
SET
Druhá funkce, „set „, umožňuje opačnou operaci pro změnu skutečných atributů z vypočteného atributu.
Vezmeme-li si příklad plného jména, jednoduchým pravidlem bude hledání mezery v atributu pro přerozdělení hodnoty ve jménu a příjmení. Samozřejmě se jedná pouze o jednoduchý příklad. Pokud plné jméno obsahuje více mezer, bude nutné použít složitější obchodní pravidlo, například použít lomítko (/), a zacházet s ním jako s prioritou.
Např: „Pablo Miguel/de la Casa del Mar“.
Výhodou je, že jednou definované pravidlo se použije systematicky, aniž byste jej museli znovu definovat podle vstupního kontextu.
Function set fullName($value: Text)
var $p : Integer
$p :=Position("/"; $value)
If ($p>0)
$p :=Position(" "; $value)
End if
If ($p>0)
This .firstname:=Substring($value; 1; $p-1)
This .lastname:=Substring($value; $p+1)
Else
This .firstname:="
This .lastname:=$value
End if
A get bez množiny? A naopak…
Funkce Get musí mít povinně přístup k vypočtenému atributu. Funkce Set je povinná, aby bylo možné tento atribut měnit, ale co když existuje jen jedna nebo druhá z těchto funkcí?
Pokud je k dispozici pouze funkce Get, lze atribut považovat za„pouze pro čtení“ a nelze jej upravovat.
Když je k dispozici pouze funkce Set, atribut lze zapsat, ale nelze jej znovu přečíst. To je princip poštovní schránky nebo hesla.
Co se děje dál?
Třetí a čtvrtá funkce query a orderBy jsou si dost podobné a zachází se s nimi stejným způsobem.
Tyto funkce nejsou povinné, ale jejich použití drasticky zvýší výkony, jak uvidíme. Pokud totiž tyto funkce nejsou definovány, bude muset 4D pro každou entitu provést funkci „get“ a poté provést sekvenční třídění. To bude pravděpodobně patřit a zdaleka to nebude optimalizované.
Je nezbytné pochopit a zapamatovat si, že vypočtené atributy nemají indexy. Přesto, pokud je jejich výpočet založen na atributech, pak tyto atributy mohou (nebo by měly) být indexovány!
Z tohoto důvodu se nabízí možnost definovat, jak má vlastně probíhat vyhledávání při spuštění dotazu nebo třídění na vypočtený atribut.
Vezmeme-li si příklad fullname, na základě atributů firstname a lastname (oba jsou indexované), bude vyhledávání typu <<fullname = „Paul Smith“ >> pravděpodobně znamenat, že <<firstname = „Paul“>> a <<lastname = „Smith“>>. Vyhledávání můžete libovolně rozšířit a rozhodnout, že vyhledávání <<fullname = „Pa Sm“>> znamená, že <<firstname začíná na „Pa“>> a <<lastname začíná na „Sm“>>. Nebo že << = „Martin“>> znamená, že buď příjmení, nebo jméno začíná na „Martin“…
Dotaz
Při provádění funkce „query“ definované pro libovolný vypočtený atribut bude přijatý parametr obsahovat dvě vlastnosti nezbytné pro pochopení požadovaného vyhledávání. S jejich znalostí lze dotaz přepsat tak, aby použil jeden (nebo více) indexovaný(é) atribut(y) a vyhnul se tak sekvenčnímu vyhledávání.
$event.operator obsahuje operátor ve formě řetězce („==“, „>=“, „<“ atd.).
$event. Value obsahuje hledanou nebo porovnávanou hodnotu, rovněž ve formě řetězce.
Výsledkem dotazovací funkce může být buď řetězec, nebo objekt.
Pokud funkce vrací řetězec, musí být tento řetězec platným dotazem.
$result:="příjmení = A@ a jméno = B@"
Pokud funkce vrací objekt, musí obsahovat dvě vlastnosti:
- .query: platný řetězec dotazu, který může obsahovat zástupné znaky (:1,:2 atd.).
- .parameters: kolekce hodnot, které mají být použity uvnitř zástupných znaků
$result:=New object("query"; $query; "parameters"; $parameters)
Podívejme se nyní, jak spravovat následující dotaz datové třídy v rámci funkce query!
$es:=ds.people.query("fullname = :1"; "Paul Smith")
Zde je kompletní kód dotazovací funkce:
Function query fullname ($event: Object) -> $result: Object
$fullname :=$event.value
$operator :=$event.operátor
$p :=Position(" "; $fullname)
If ($p>0)
$firstname :=Substring($fullname; 1; $p-1)+"@"
$lastname :=Substring($fullname; $p+1)+"@"
$parameters :=New collection($firstname; $lastname)
Else
$fullname :=$fullname+"@"
$parameters :=New collection($fullname)
End if
Case of
: ($operator="==") | ($operator="===")
If ($p>0)
$query :="jméno = :1 a příjmení = :2"
Else
$query :="jméno = :1 nebo příjmení = :1"
End if
: ($operator="!=")
If ($p>0)
$query :="jméno != :1 a příjmení != :2"
Else
$query :="jméno != :1 a příjmení != :1"
End if
End case
$result :=New object("query"; $query; "parameters"; $parameters)
Order By
Co se týče řazení, je to dost podobné. V tomto případě je řazení lidí podle věku úplně stejné jako řazení podle převráceného data narození. Takže při požadavku na seřazení podle věku bude chytré použít datum narození, které je indexováno jako kritérium.
- Řazení podle vzorků:
$es:=ds.people.all().orderBy("age desc")
$es :=ds.people.all().orderBy("age asc")
- Funkce OrderBy:
Function orderBy age($event: Object) -> $result: String
If($event.operator = "desc")
$result:="datum narození asc"
Else
$result :="datum narození desc"
End if
Použití složených indexů
Složené indexy lze v některých případech použít velmi pozitivně. Připomeňme si, že: Při řazení podle „příjmení“ a „jména“ se nepoužívá index příjmení ani index jména. Aby byl tento druh třídění optimalizován, musí existovat index předchozí jméno+první jméno nebo jméno+příjmení… nebo obojí.
V případě vypočteného atributu, jako je plné jméno, použije 4D tento složený index, pokud si je vědom, že se ve skutečnosti jedná o třídění křestní jméno + příjmení! Dokonce je možné vynutit třídění podle příjmení + j ména, i když fullname zobrazuje křestní jméno před příjmením!
- Řazení podle vzorku:
Form.people:=ds.people.all().orderBy("fullname asc")
- Funkce OrderBy:
Function orderBy fullname($event : Object)->$result : Text
If ($event.descending)
$result :="příjmení desc, jméno desc"
Else
$result :="příjmení asc, jméno asc"
End if
Další možné typy atributů COMPUTED
Ve výše uvedených kapitolách jsme se zabývali vypočtenými atributy skalárního typu (textové, číselné…), ale vypočtenými atributy mohou být také objekty, entity nebo výběry entit!
Některé možné příklady:
- fullAddress: objekt s tolika atributy, kolik je potřeba (celé jméno, ulice, poštovní směrovací číslo, město, země atd.).
Function get fullAddressThis($event: Object) -> $result: Object
$result :=New object
$result .fullName:=This.fullName // Another computed attribute can be used!
$result .address:=This.address
$result .zipCode:=This.zipCode
$result .
city:=This.city
$result.
state:=This.state
$result .
country:country
- bigBoss: entita
Function get bigBoss($event: Object) -> $result: cs.peopleEntity
$result :=this.manager.manager
- coworkers : entitySelection
Function get coworkers($event: Object) -> $result: cs.peopleEntitySelection
$result :=this.manager.directReports.minus(this)
Závěr
Vypočtené atributy se rodí a dobře rodí! Přinášejí jak flexibilitu a sílu, tak i větší kontrolu nad tím, co je či není přístupné. Přístup k těmto atributům je optimalizován tak, aby nebyl penalizován ani paměťový, ani síťový přístup. Jsou jednoduchým řešením obchodních požadavků a splňují zvýšené požadavky moderního programování.
Podrobnější informace naleznete v této dokumentaci.