Uno spettacolo magico vi aspetta con gli attributi calcolati di ORDA!

Tradotto automaticamente da Deepl

Spesso è utile o addirittura essenziale che le banche dati si adattino in modo flessibile e in evoluzione agli utenti e alle loro attività nel mondo informatico. Anche il controllo dei dati accessibili è un argomento ricorrente e delicato. Da questo punto di vista, gli sviluppatori utilizzano metodi e formule talvolta complessi per dare o limitare l’accesso alle informazioni, a seconda del contesto o dei diritti di accesso degli utenti.

Facciamo un semplice esempio. Nella vostra applicazione, a volte avete bisogno di visualizzare un elenco di persone. Una delle colonne visualizza i loro nomi completi, ma nel database sono presenti un campo nome e un campo cognome. Attualmente, si scrive una formula nella colonna della casella di riepilogo e si deve gestire da soli l’ordinamento della colonna. Non sarebbe bello avere un campo calcolato in cui definire la formula di calcolo e il metodo di ordinamento e avere tutta la logica aziendale all’interno della classe e non in ogni interfaccia?

A partire da 4D v19 R3, 4D offre una soluzione a questo problema, con gli attributi calcolati.

Attributi calcolati HDI ORDA

DEMO Attributi calcolati ORDA

IMMAGINA…

Avete un elenco di persone di cui conoscete il cognome, il nome, la data di nascita, ecc.

Immaginiamo di voler visualizzare, in un modulo, un elenco di persone con il loro nome completo (basato su nome + cognome), la loro età (basata sulla data di nascita), la loro foto attuale (basata sulla loro età)…
Poi, non appena si seleziona una persona, si possono visualizzare le informazioni sui genitori, i nonni, i figli, i fratelli e le sorelle, gli zii e le zie, senza dimenticare i cugini!

È possibile? Sì, certo.
Senza complicate righe di codice nel modulo? Sì!

Tutta questa magia è possibile grazie agli attributi calcolati, sui quali è anche possibile effettuare ricerche e ordinamenti, proprio come con gli altri attributi! Diamo un’occhiata più da vicino!

Definizione e calcoli

Per spiegare la potenza di questo concetto, inizieremo con un esempio concreto. Una classe di dati “persone” contiene classicamente attributi come “cognome”, “nome”, “indirizzo”, “codice postale”, “città”, “paese”, “data di nascita”, ecc.
Da questa classe, possiamo avere bisogno del nome completo (cognome + nome), dell’età (in base alla data di nascita) o dell’indirizzo completo (come oggetto).
E possiamo anche andare oltre. Se le relazioni lo consentono, gli attributi calcolati possono anche essere del tipo di entità (ad esempio, padre o madre) o della selezione di entità (figli, genitori, nonni, fratelli, ecc.).

L’accesso a questi attributi sarà possibile attraverso la classe “peopleEntity”, nella quale dovranno essere definite nuove funzioni a questo scopo.

Saranno richiamati quando 4D avrà bisogno di accedere a questi attributi, sia per leggerli (ad esempio, per una semplice visualizzazione), sia durante una ricerca o un ordinamento, sia infine durante un eventuale salvataggio dopo una modifica.
Il risultato di attributi calcolati, come l’indirizzo completo, può richiedere un altro attributo calcolato, come il nome completo. Anche questo tipo di ricorsione è possibile; torneremo su questo punto.

Queste functions (get, set e query e orderBy che verranno discusse in seguito) devono essere definite all’interno della classe entità stessa (es: peopleEntity).

Accesso agli attributi calcolati

Ottenere

La prima funzione, “get”, consente di definire il modo in cui l’attributo viene calcolato. A tale scopo, deve restituire il risultato del calcolo.
Questa funzione è l’unica obbligatoria per utilizzare gli attributi calcolati. Infatti, è solo la sua presenza a determinare l’esistenza stessa di questo attributo.

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

Esposto, locale … o no?

Come sarà accessibile questo attributo e in quali condizioni sarà calcolato?

La parola chiave “exposed” che può essere usata per definire la funzione è l’equivalente della casella di controllo “Exposed” nell’ispettore dell’editor di strutture.

blank

La parola chiave “locale”, utilizzata in modalità client-server, determina se il calcolo deve essere eseguito sistematicamente sul server o se deve essere condotto localmente per limitare l’accesso alla rete.

SET

La seconda funzione, “set”, consente l’operazione opposta di modificare gli attributi reali a partire da un attributo calcolato.
Se prendiamo l’esempio di fullname, la regola semplice sarà quella di cercare uno spazio nell’attributo per ridistribuire il valore in firstname e lastname. Naturalmente, questo è solo un semplice esempio. Se il nome completo contiene diversi spazi, si dovrà applicare una regola commerciale più complessa, come l’uso di una barra (/), trattandola come una priorità.
Ad esempio: “Pablo Miguel/de la Casa del Mar”.
Il vantaggio è che una volta definita, questa regola verrà applicata sistematicamente senza doverla ridefinire in base al contesto di input.

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

Un get senza un set? E il contrario…

Una funzione Get è obbligatoria per avere accesso a un attributo calcolato. Una funzione Set è obbligatoria per rendere modificabile questo attributo, ma cosa succede se esiste solo una o l’altra di queste funzioni?

Quando è disponibile solo Get, l’attributo può essere consideratodi “sola lettura” e non può essere modificato.

Quando è presente solo Set, l’attributo può essere scritto ma non può essere riletto. Questo è il principio di una casella di posta elettronica o di una password.

Cosa succede dopo?

La terza e la quarta funzione query e orderBy sono piuttosto simili e vengono trattate allo stesso modo.
Queste funzioni non sono obbligatorie, ma il loro utilizzo aumenterà drasticamente le prestazioni, come vedremo. Infatti, se queste funzioni non sono definite, 4D dovrà eseguire la funzione “get” per ogni entità e poi eseguire un ordinamento sequenziale. È probabile che questa operazione appartenga e sia tutt’altro che ottimizzata.

È essenziale capire e ricordare che gli attributi calcolati non hanno indici. Tuttavia, se il loro calcolo è basato su attributi, allora questi attributi possono essere (o dovrebbero essere) indicizzati!

Per questo motivo, viene offerta la possibilità di definire come deve essere effettuata la ricerca quando viene lanciata una query o un ordinamento su un attributo calcolato.

Se prendiamo l’esempio di fullname, basato sugli attributi firstname e lastname (entrambi indicizzati), una ricerca del tipo <<fullname = “Paul Smith” >> significherà probabilmente che <<firstname = “Paul”>> e <<lastname = “Smith”>>. Si è liberi di estendere la ricerca e decidere che una ricerca <<fullname = “Pa Sm” >> significa che <<firstname inizia con “Pa”>> e <<lastname inizia con “Sm”>>. Oppure che << = “Martin” >> significa che il cognome o il nome inizia con “Martin”…

Interrogazione

Quando si esegue la funzione “query” definita per qualsiasi attributo calcolato, il parametro ricevuto conterrà due proprietà necessarie per comprendere la ricerca desiderata. Sapendo questo, la query può essere riscritta per utilizzare uno (o più) attributi indicizzati ed evitare così una ricerca sequenziale.

$event.operator contiene l’operatore in forma di stringa (“==”, “>=”, “<“, ecc.)
$event. Value contiene il valore da ricercare o confrontare, anch’esso in forma di stringa.

Il risultato di una funzione di query può essere una stringa o un oggetto.

Se la funzione restituisce una stringa, questa deve essere una query valida.

$result:="cognome = A@ e nome = B@"

Se la funzione restituisce un oggetto, questo deve contenere due proprietà:

  • .query: una stringa di query valida che può contenere segnaposto (:1,:2, ecc.)
  • .parameters: un insieme di valori da utilizzare all’interno dei segnaposto

$result:=New object("query"; $query; "parameters"; $parameters)

Vediamo ora come gestire la seguente query di classe di dati all’interno della funzione query!

$es:=ds.people.query("fullname = :1"; "Paul Smith")

Ecco il codice completo della funzione query:

Function query fullname ($event: Object) -> $result: Object
$fullname :=$event.value
$operator :=$event.operatore
$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 :="nome = :1 e cognome = :2"
Else
$query :="nome = :1 o cognome = :1"
End if
: ($operator="!=")
If ($p>0)
$query :="firstname = :1 e lastname = :2"
Else
$query :="firstname = :1 e lastname = :1"
End if
End case
$result :=New object("query"; $query; "parameters"; $parameters)

Ordina per

Per quanto riguarda l’ordinamento, è abbastanza simile. In questo caso, ordinare le persone in base all’età è esattamente come ordinare le persone in base alla loro data di nascita. Quindi, quando viene richiesto un ordinamento per età, sarà intelligente usare la data di nascita, che è indicizzata come criterio.

  • Ordina per campioni:

$es:=ds.people.all().orderBy("age desc")
$es :=ds.people.all().orderBy("età asc")

  • Funzione OrderBy:

Function orderBy age($event: Object) -> $result: String
If
($event.operator = "desc")
$result:= "compleanno asc"
Else
$result
:= "compleanno desc"
End if

Uso degli indici composti

Gli indici composti possono essere utilizzati in modo estremamente positivo in alcuni casi. Ricordate: Un ordinamento per “cognome” e “nome” non utilizza l’indice del cognome o del nome. Affinché questo tipo di ordinamento sia ottimizzato, deve esistere un indice nome+nome precedente, o nome+cognome… o entrambi.

Nel caso di un attributo calcolato come fullname, 4D utilizzerà questo indice composito se sa che si tratta in realtà di un ordinamento nome+cognome! È persino possibile forzare un ordinamento per cognome + nome, anche se il nome completo mostra il nome prima del cognome!

  • Ordinamento per campione:

Form.people:=ds.people.all().orderBy("fullname asc")

  • Funzione OrderBy:

Function orderBy fullname($event : Object)->$result : Text
If ($event.descending)
$result :="lastname desc, firstname desc"
Else
$result :="lastname asc, firstname asc"
End if

Altri possibili tipi di attributi COMPUTATI

Nelle sezioni precedenti, abbiamo trattato gli attributi calcolati di tipo scalare (testo, numerico…), ma gli attributi calcolati possono anche essere oggetti, entità o selezioni di entità!

Alcuni esempi possibili:

  • fullAddress: oggetto con tutti gli attributi necessari (nome completo, via, codice postale, città, paese, ecc.)

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: entità

Function get bigBoss($event: Object) -> $result: cs.peopleEntity
$result :=this.manager.manager

  • collaboratori : entitySelection

Function get coworkers($event: Object) -> $result: cs.peopleEntitySelection
$result :=this.manager.directReports.minus(this)

Conclusione

Gliattributi calcolati sono nati e sono ben nati! Essi apportano flessibilità e potenza, nonché un maggiore controllo su ciò che è accessibile o meno. L’accesso a questi attributi è ottimizzato in modo da non penalizzare né la memoria né l’accesso alla rete. Sono la soluzione più semplice alle esigenze aziendali e soddisfano i maggiori requisiti della programmazione moderna.

Per maggiori dettagli, leggete questa documentazione.

Roland Lannuzel
- Product Owner ed esperto di 4D - Dopo aver studiato elettronica, Roland è passato all'IT industriale come sviluppatore e consulente, realizzando soluzioni per i clienti con una varietà di database e tecnologie. Alla fine degli anni '80 si è innamorato di 4D e lo ha utilizzato per scrivere applicazioni aziendali che includono sistemi di contabilità, fatturazione e posta elettronica. Entrato a far parte dell'azienda nel 1997, Roland ha dato il suo prezioso contributo progettando specifiche, strumenti di test e demo, oltre a formare e parlare alla comunità 4D in occasione di numerose conferenze. Continua a plasmare attivamente il futuro di 4D definendo nuove funzionalità e strumenti di sviluppo di database.