vai al contenuto principale

Modella relazioni

Estendiamo il modello con ulteriori classi tra loro correlate mediante relazioni di associazione e composizione.

Nella lezione precedente #

Abbiamo concluso la nostra digressione sull’importazione ed esplorazione dei dati. Riportiamo il focus sulla modellazione e ricapitoliamo cosa abbiamo visto fino a ora:

  • abbiamo modellato la classe Employee per gestire dati anagrafici di un insieme di impiegati. Attualmente è l’unica classe presente;
  • sul Database Schema, abbiamo modellato attributi nativi con vincoli e domini e dichiarato espressioni per rappresentare attributi derivati;
  • sull’Application Schema abbiamo definito Class warning e personalizzato il layout della form di inserimento e lettura di employee.

Qualora non avessi chiari gli argomenti elencati sopra, prima di procedere ti consigliamo di ritornare sulle lezioni Crea la tua prima applicazione ed Estendi la tua applicazione.

Da una a più classi #

In questa lezione estendiamo il nostro modello di partenza rappresentando nuovi concetti (e quindi nuove classi) tra loro correlati. Nei database relazionali è infatti comune che informazioni distinte (rappresentate su più tabelle) siano in qualche modo legate tra loro da una forma di relazione; ad esempio, un impiegato fa parte di un team, un impiegato partecipa a un progetto con un incarico di tot mesi, un progetto ha una lista di fatture, etc… Ci concentriamo quindi nell’espandere l’insieme di concetti satellite di Employee, come progetti, qualifiche, team e indirizzi; nel farlo, introdurremo nuove classi da collegare a Employee, modellando relazioni di diverso tipo e cardinalità.

Le associazioni e i ruoli #

Dalla Dashboard, arresta la Cloudlet Workforce e apri il modello TutorialEngine nel Designer, assicurandoti di aver selezionato la vista Database.

1. Aggiungi una nuova classe #

Per iniziare, rappresentiamo i progetti dell’azienda; per farlo, dobbiamo aggiungere una nuova classe, che chiamiamo Project:

  • Aggiungiamo degli attributi: ogni progetto ha un nome (name: string), che è anche l’object title della classe, e un flag (completed: boolean) che indica se il progetto è concluso o meno. Spunta required per il primo; per il secondo invece, dall’Application Schema , imposta il default value come costante booleana falsa.
  • Rendiamo i progetti unici, ma questa volta, invece di imporre un vincolo sul nome, sfruttiamo il tipo di dato serial, che permette di aggiungere un identificatore progressivo, univoco ed esplicito. Aggiungi quindi un attributo serial: serial e nota come su di esso viene imposto automaticamente un vincolo di unicità () che non può essere rimosso o modificato manualmente.

La classe Project appare come in figura. Attraverso ciascuna classe stiamo ora rappresentando un insieme indipendente di informazioni.

Designer project partial

2. Aggiungi un’associazione tra due classi #

Cursor database association bidirectional Colleghiamo la classe appena creata a Employee: clicca sull’icona della Palette Create a new bidirectional association; clicca su una classe e poi sull’altra (in questo caso l’ordine non è importante).

Abbiamo appena creato un’associazione, la più comune forma di relazione tra due classi. Nel modello, una relazione è rappresentata da una linea che connette una coppia di classi. Ciascuna delle classi che partecipano all’associazione assume un ruolo (role), raffigurato con un cerchio all’estremità della relazione.

Designer relations employee project

Pensa ai ruoli come a degli attributi “speciali”: quando un ruolo è selezionato nel diagramma, viene evidenziata anche la classe di cui quel ruolo fa parte. Questa classe è chiamata parent per quel ruolo (in alcuni casi è anche detta classe source), mentre la classe che quel ruolo rappresenta è chiamata target.

Nel caso in figura, cliccando sul ruolo di sinistra viene evidenziato il parent Employee (un impiegato, oltre ai suoi attributi, è caratterizzato da un insieme di progetti a cui lavora), mentre Project è la classe target puntata dal ruolo.

Designer relations employee project highlighted

3. Cardinalità, identificatori e navigabilità dei ruoli #

Il simbolo all’interno del ruolo rappresenta la sua cardinalità, ovvero quanti oggetti di quella classe possono essere associati a un oggetto della classe a cui quel ruolo si riferisce. Di default, le associazioni vengono create come molti-a-molti: l’asterisco * (any) sul ruolo della classe A indica che ogni oggetto A può essere associato a un numero qualunque di oggetti della classe B. Nel nostro caso, un impiegato può lavorare su un numero qualunque (anche nullo) di progetti.

Possiamo modificare la cardinalità di un ruolo dal Role menu, a cui possiamo accedere facendo click destro su di esso; combinando le cardinalità dei due ruoli possiamo definire associazioni uno-a-molti, uno-a-uno, oppure con cardinalità personalizzate. Per ora non modificare la cardinalità di questa associazione.

Designer role menu

Ciascun ruolo, analogamente a classi e attributi, ha un suo identificatore:

Puoi esaminare l’identificatore dal tooltip che appare passando col mouse sopra al ruolo. In questo caso, il cerchio dal lato di Project rappresenta il ruolo _project__ di Employee nell’ambito dell’associazione con Project, cioè _Employee.project__. Nel tooltip sono inoltre descritte le regole di cardinalità di quel ruolo in modo discorsivo, come in figura.

Designer project association tooltip

Per favorire la pluralizzazione, rinominiamo i ruoli: fai click destro sul ruolo _project__ e seleziona Rename dal Role menu; digita projects e premi Invio per confermare; ripeti il procedimento per il ruolo di _Employee_ e rinominalo in employees. Gli identificatori che abbiamo scelto riflettono la cardinalità dell’associazione: un employee lavora su più projects, e a un project lavorano più employees.

Alle estremità dell’associazione sono presenti due frecce. Queste indicano la navigabilità dell’associazione; in questo caso l’associazione è visibile da entrambe le classi ed è pertanto bidirezionale, ovvero è possibile da un impiegato risalire ai progetti a cui lavora, e viceversa. Dal Role menu possiamo spuntare Navigable per rimuovere la visibilità da una delle due classi e rendere l’associazione unidirezionale. Per ora, lascia l’associazione tra Employee e Project così com’è.

4. Usa le associazioni per estrarre nuove classi #

Osservando la classe Employee possiamo notare facilmente che esistono due problemi:

  • Ogni impiegato può ricoprire una più una sola position tra quelle che abbiamo consentito nella lista di valori ammessi. Cosa succede se vogliamo aggiungere nuove posizioni? Cosa succede se vogliamo permettere di ricoprire più di una posizione?
  • L’attributo team ha spesso un valore ripetuto, ma questo deve essere ogni volta inserito manualmente. Come facciamo a impedire di inserire nomi di team errati senza dichiarare a priori quali team sono ammissibili restringendo il dominio?

I problemi nascono dal fatto che in realtà informazioni come posizione e team non sono parte integrante del concetto di impiegato. Pertanto in questo caso le restrizioni del dominio non sono la strada giusta, perché dovremmo modificare il modello ogni volta che un utente vuole aggiungere una nuova posizione o un team dall’applicazione.

Sfruttiamo le relazioni per risolvere entrambi i problemi e aggiungiamo due nuove classi per “incapsulare” le informazioni relative a questi attributi:

  • Per prima cosa elimina gli attributi position e team da Employee. Nota come l’eliminazione degli attributi position e team comporterà la rimozione delle corrispondenti colonne dal database e dei valori in esse memorizzati. A fine modellazione dovremo risolvere questa issue di media entità.
  • Crea le classi Qualification e Team e aggiungi l’attributo label: string per Qualification e name: string per Team.
  • Imposta entrambi gli attributi come object title per le rispettive classi, spuntali come obbligatori e imponi inoltre un vincolo di unicità su ciascuno (non vogliamo due oggetti che rappresentino la stessa qualifica o due team con nomi identici).

Designer team qualification

Associamo Qualification a Employee in modo che un impiegato abbia al massimo tre qualifiche e che una qualifica possa essere condivisa da un numero qualsiasi di impiegati. Vogliamo inoltre che sia possibile risalire da un impiegato alla sua qualifica, ma non viceversa. Pertanto, creiamo un’associazione unidirezionale:

  • Cursor database association unidirectional clicca sull’icona Create a new unidirectional association, clicca prima su Employee e poi su Qualification. Stavolta l’ordine è importante, perché determina il verso dell’associazione;
  • apri il Role menu per il ruolo di Qualifications e seleziona Custom Cardinality: dal Cardinality Editor imposta 0 per MIN e 3 per MAX. Il ruolo riporterà la lettera C a indicare la cardinalità personalizzata;
  • infine, rinomina questo ruolo in qualifications e quello di Employee in employees.

Associamo Team ad Employee in modo che un team abbia almeno un impiegato e che un impiegato appartenga al massimo a un team, stavolta consentendo la navigabilità in entrambe le direzioni (da un team si può risalire ai suoi membri):

  • crea un’associazione bidirezionale tra le due classi e scegli le cardinalità One or more (1N) per il ruolo di Employee e Zero or one (01) per quello di Team;
  • rinomina i ruoli rispettivamente in members e team (con una cardinalità a uno ha senso usare un identificatore al singolare).

Dopo queste modifiche, il modello appare come in figura:

Designer relations employee project qualification team

5. Aggiungi un’associazione riflessiva #

Supponiamo esista una gerarchia tra gli impiegati, per la quale alcuni devono supervisionare il lavoro di altri. Per rappresentare questa gerarchia potremmo creare delle qualifiche “speciali” – responsabile tecnico, CEO, capo progettista – che potrebbero essere ricoperte da un numero limitato di impiegati; tuttavia in questo modo stabiliremmo una gerarchia implicita basata sul livello di importanza di queste posizioni.

Nel nostro caso, considerando anche la presenza di più team, ci farebbe più comodo poter dire direttamente “chi è il supervisore di chi”, mettendo gli impiegati in relazione tra loro. Una relazione di questo tipo è detta riflessiva, poiché lega oggetti che appartengono alla stessa classe.

Per aggiungere un’associazione riflessiva, seleziona come sempre l’icona ma clicca poi due volte su Employee. Apparirà automaticamente un anello in basso a destra con due ruoli sulla stessa classe, chiamati di default employee_Source ed employee_Target; rinomina quest’ultimo in supervisor e cambia la cardinalità in Zero or one (01). Se non l’hai già fatto, rendi l’associazione unidirezionale da Source al Target.

Designer relations employee project qualification team recursive

In questo modo abbiamo stabilito che un impiegato può opzionalmente indicare un suo diretto supervisore. Ciascun impiegato può essere indicato come supervisore da più persone; da un impiegato possiamo risalire al suo supervisore, ma non viceversa.

Migliora la leggibilità del diagramma #

Spesso un modello viene aperto e modificato da più persone, e il significato di alcuni suoi elementi possono risultare poco chiari a chi non li ha modellati. Per fornire informazioni aggiuntive, possiamo annotare il diagramma con dei commenti.

Designer employee supervisor comment 1

Cursor database comment Aggiungiamo un commento al ruolo Employee.supervisor : seleziona l’icona della Palette Create a new comment e clicca sul diagramma; nel post-it che appare, digita “Supervisor” e clicca altrove per confermare.

Cursor database comment link Clicca sul commento appena creato e poi sul ruolo Employee.supervisor, per collegarlo a quest’ultimo. Ora puoi spostare il commento trascinandolo per la maniglia a sinistra, ridimensionarlo con la maniglia nell’angolo in fondo a destra e piegare il link a piacere.

Scegli i widget per rappresentare i ruoli #

In precedenza, quando avevamo solo la classe Employee nel modello, abbiamo usato alcuni semplici widget per modificare le rappresentazioni dei suoi attributi all’interno della form, usando il Form layout editor. Possiamo fare la stessa cosa con i ruoli che adesso interessano Employee.

Spostati sulla vista di Application e apri il suo Form layout editor: sono presenti i quattro nuovi campi qualifications, team, projects e supervisor che contengono i widget per le classi target Qualification, Team, Project e la stessa Employee. Si tratta di quattro widget differenti, che però possono essere cambiati come vedremo nella sezione successiva.

1. Scegli i widget nella form #

Facendo click destro su un ruolo e selezionando Gui icons edit Edit properties puoi aprire il pannello delle Properties per il suo widget. Da questo pannello possiamo scegliere quale widget usare e personalizzarne l’aspetto.

Scegliamo i widget più adatti per la form di Employee.

  1. scegli Set of dropdown menus per qualifications e imposta a 3 il numero di elementi visibili;
  2. scegli Single selection listbox per team;
  3. scegli Set of checkboxes per projects e imposta a 5 il numero di elementi visibili;
  4. lascia Single row table per supervisor.

Personalizziamo il layout del widget per quest’ultimo ruolo: dalle Properties di supervisor spunta l’opzione Use custom list layout e clicca su Open per aprire il List layout editor.

Designer employee role supervisor list layout editor

Questo pannello ci consente di modificare l’aspetto di una Livetable in modo analogo a quello che possiamo fare nell’applicazione (ordinare, nascondere e raggruppare colonne). L’unica differenza è che le modifiche a una vista da Designer sono globali, mentre le modifiche dall’applicazione valgono solo per l’utente che le ha apportate. Facciamo il punto della situazione: stiamo personalizzando il ruolo supervisor il cui target è Employee, perciò nel pannello compaiono gli attributi di questa classe. Per il supervisore ci interessa mostrare solo il nome e il contatto, pertanto trascina tutte le colonne dalla lista Data columns alla lista Initially hidden, eccetto full_name, phone_number e email_address.

Una volta completate le modifiche, la form dovrebbe apparire come in figura. Nota come la tabella Employee target di supervisor mostri solo le colonne che ci interessano in questo contesto!

Designer employee application form layout editor modified with association managers

2. Scegli i widget di default #

Chiudiamo l’editor e torniamo sul diagramma: dal Class menu di Employee clicca su Configure GUI widgets per aprire il GUI widgets configurator.

Osserviamo le schede Association to one e Association to many: da qui possiamo scegliere con quale widget è rappresentata la classe Employee quando si comporta da target in un’associazione a uno o a molti. In questo modo, ipotizzando di voler personalizzare l’aspetto di Employee anche in altre form, possiamo stabilire una configurazione base [tipo di widget + layout] evitando di dover ripetere le modifiche in ciascuna form che usa quella classe.

Designer employee gui widget configurator association to one

Per il momento non modificare alcun valore per Employee e osserva invece la scheda Management:

Designer employee gui widgets configurator

Questa scheda permette di scegliere il widget di default con cui rappresentare il gestore di classe di Employee, ovvero il componente usato per elencare, accedere, creare, modificare e rimuovere i suoi record e che è associato a un ulteriore ruolo speciale, definito class role. Notiamo che il widget di default è adesso una Livetable.

Clicca su Config... per accedere al List layout editor della Livetable. Trascina full_name da Data columns a Initially hidden, così da rendere questa colonna nascosta di default per tutti gli utenti, e chiudi l’editor.

Le composizioni #

Finora, lavorando con le associazioni, abbiamo messo in relazione le nostre classi dando a ciascuna coppia uguale importanza. La composizione è invece una relazione in cui una classe appartiene o è parte dell’altra.

Aggiungiamo ulteriori elementi al modello e supponiamo di voler sofisticare la relazione che c’è tra un impiegato e i progetti a cui collabora, memorizzando la data di inizio e di fine dell’incarico. Supponiamo che un impiegato possa nel tempo avere più incarichi relativi allo stesso progetto; per reificare queste informazioni dobbiamo introdurre una nuova classe intermedia tra Employee e Project:

Per prima cosa, liberiamoci dell’associazione bidirezionale che avevamo inizialmente creato: fai click destro su di essa e seleziona Delete dall’Association menu.

Dopodiché, crea una classe Project_assignment per reificare l’associazione tra Employee e Project; aggiungi due attributi start_date: date e end_date: date e rendi il primo obbligatorio (possono esistere incarichi che non hanno ancora una data di fine, ma devono sempre avere una data di inizio). Per il momento rendi start_date object title.

Designer project assignment

Imponiamo un Class warning dall’Application Schema sulla nuova classe per impedire agli utenti di inserire una data di fine incarico precedente alla data di inizio: crea un warning su Project_assignment chiamato endDateInPast con espressione end_date < start_date che viene calcolato durante una SaveNew e una SaveExisting; digita il messaggio End date must be after the start date. e rendi il warning bloccante.

Designer warning date in past

1. Aggiungi una composizione #

Ora dobbiamo mettere Project_assignment in relazione con Employee e Project, presumibilmente tramite una coppia di associazioni; ragioniamo però su questa domanda: quanto è forte la dipendenza che una classe ha nei confronti dell’altra? Mentre Project e Employee sono concetti ben distinti (possono essere gestiti indipendentemente), un incarico ha senso di esistere solo se c’è un impiegato a svolgerlo. In casi come questo è più corretto usare una composizione.

Cursor database compositionSpostati sul Database Schema e clicca sull’icona della Palette Create a new composition, poi clicca prima su Employee e dopo su Project_assignment.

Designer relations composition employee project assignment

Come puoi notare in figura, questo tipo di relazione presenta un ruolo speciale ed è navigabile in una sola direzione. Il rombo presso Employee indica che questa relazione è una composizione da Employee a Project_assignment. In una composizione, il target di questo ruolo è detto classe part (la parte), mentre il parent è detto classe whole (il tutto). Rinomina il ruolo part in assignments e lascia la cardinalità di default a molti (*) (un impiegato può avere più incarichi).

Nota come siano scomparsi da Project_assignment sia il default class role che la cardinalità di classe; aggiungendo la composizione, infatti, abbiamo stabilito una dipendenza secondo cui non può esistere un incarico senza un impiegato che lo svolga; pertanto non sarà possibile creare oggetti project_assignment senza prima creare un oggetto employee.

Il prossimo passo è mettere in relazione Project_assignment e Project:

  • in questo caso crea una semplice associazione unidirezionale rivolta dalla prima alla seconda classe. Rinomina il ruolo della prima in assignments;
  • imposta la cardinalità del ruolo della seconda a Exactly one (1): ciascun incarico deve sempre far riferimento a un progetto, e un progetto può essere riferito da più incarichi.

Le tre classi appaiono come in figura:

Designer relations employee project project assignment

Project_assignment reifica l’associazione da Employee verso Project aggiungendo informazioni come le date di inizio e fine dell’incarico. Naturalmente, così facendo abbiamo tolto, dalla classe Project, la possibilità di risalire agli impiegati che partecipano ai progetti.

Supponiamo ora che uno di questi impiegati ricopra la speciale posizione di direttore per uno specifico progetto, e che sia necessario poter risalire alle sue informazioni con un riferimento “diretto” che non passi per Project_assignment. Per fare ciò dobbiamo introdurre una nuova associazione più specifica tra Project ed Employee:

  • crea un’altra associazione unidirezionale rivolta da Project a Employee;
  • imposta a Exactly one (1) la cardinalità del ruolo di Employee: un progetto ha un solo direttore, un impiegato può dirigere più progetti;
  • rinomina i ruoli rispettivamente in projects e director.

Una volta completate le modifiche, il diagramma appare come in figura:

Designer relations project project assignment employee qualification team

2. Composizioni e forme di riuso #

Una strategia diffusa nella modellazione è il riuso di elementi. Per prima cosa, supponiamo che un progetto possa essere stato richiesto da un committente esterno all’azienda:

Crea una classe Customer con due attributi: l’object title name: string per indicare la ragione sociale del cliente o dell’organizzazione, e address: string, che conterrà un generico indirizzo.

Designer customer

Collega Project a Customer con un’associazione bidirezionale, rinomina i ruoli rispettivamente in projects e customer e imposta la cardinalità di quest’ultimo a Zero or one (01). In questo modo permettiamo di registrare progetti senza committente, come nel caso di progetti interni all’azienda.

Designer relations project customer

Ora supponiamo di voler memorizzare un indirizzo anche per i nostri impiegati. Una soluzione potrebbe essere quella di aggiungere un altro attributo address per Employee. Tuttavia questo approccio comporta diversi problemi:

  • Cosa succede se vogliamo che i due attributi abbiano una struttura comune?
  • Cosa succede se ripetiamo il ragionamento considerando un insieme di attributi, invece che uno solo (ad esempio, usando più attributi relativi all’indirizzo per memorizzare anche informazioni come la città o il paese)?
  • Cosa succede se in futuro vogliamo rappresentare più indirizzi per un dipendente (ad esempio, indirizzo di lavoro e indirizzo di residenza)?

Evidentemente un approccio del genere non può scalare. Una soluzione più flessibile è quella di incapsulare le informazioni in un’unica classe, da collegare a Employee e Customer con una coppia di composizioni a uno (e risolvere i tre problemi di sopra):

  • crea una nuova classe Address, trascina l’attributo address di Customer e rilascialo sulla nuova classe cliccando su Move here;
  • aggiungi gli attributi zip_code: string, city: string e country: string per memorizzare CAP, città e nazione;
  • imponi poi una restrizione sul dominio di zip_code affinché sia una sequenza di lettere e numeri lunga esattamente cinque caratteri: usa l’espressione regolare ^[A-Za-z0-9]+$,e imposta sia Min length che Max length a 5);

Designer address

  • crea un object title composto dalla concatenazione dei valori tutti e quattro gli attributi (ti ricordi come si fa?);
  • collega Customer ed Employee ad Address con due composizioni, rinominando per entrambe il ruolo in address e impostando la cardinalità a Exactly one (1). In questo modo abbiamo riutilizzato degli attributi comuni racchiudendoli in una classe part condivisa da più classi whole.

Designer relations composition employee address customer

3. Composizioni multiple e vincoli di unicità #

La cardinalità “esattamente a uno” è molto stringente, e prevede che per ciascun impiegato o committente debba sempre corrispondere un indirizzo. Ciò non vuol dire che impiegato e committente debbano condividere lo stesso indirizzo; anzi non possiamo mai avere un oggetto address che è part sia di un employee sia di un customer! Quando creiamo uno dei due oggetti whole, viene creato automaticamente anche un address part per quell’unico oggetto.

Tuttavia, nulla ci impedisce di memorizzare un address con tutti i campi vuoti, oppure di creare due address con tutti i campi identici. Proviamo a impedire che si verifichi il secondo caso e creiamo un vincolo di unicità su tutti e quattro gli attributi: selezionali e clicca su Make unique.

Un nuovo selettore Unique constraint options for parts appare sullo schermo: in caso di composizioni multiple come questa, il Designer ci permette di definire un vincolo di unicità specifico, in base alla “provenienza” dell’oggetto part. In questo modo possiamo evitare l’effetto collaterale per cui le informazioni di una classe andrebbero in conflitto con quelle di un’altra.

Designer address unique constraints options for parts

Seleziona l’opzione Only among 'address' belonging to the same 'Customer'; dopodiché, ripeti lo stesso procedimento selezionando invece Only among 'address' belonging to the same 'Employee'; appaiono due chiavi nel footer di Address; cliccando su una di esse viene evidenziato – oltre agli attributi coinvolti – anche il rombo della classe whole per cui quel vincolo è valido.

Designer relations composition employee address customer unique constraints

4. Composizioni e widget #

Torniamo rapidamente nell’Application Schema per parlare dei widget relativi alle composizioni (Composition Managers). Apri di nuovo il Form layout editor di Employee (l’unica classe ad avere sia una composizione a molti che a uno) e osserva i pannelli properties per i due ruoli project_assignments e address:

Designer employee application form layout editor modified with composition managers

  • Come abbiamo accennato parlando del concetto di dipendenza tra classi, nella vista applicativa non ci sarà una voce di menu per Project_assignment (così come invece è presente per Employee e ci sarà per Project, Team e le altre nuove classi); tuttavia dal gestore d’istanza di un singolo oggetto employee potremo accedere al suo insieme di assignments e gestirli da quella posizione. Pertanto, gli unici widget disponibili per le composizioni a molti sono le viste tabellari che ne consentono la gestione completa (Livetable e Editable).
  • Il ruolo di una composizione a uno stringente – come address, che ha cardinalità Exactly one – non usa alcun widget: la classe part viene “inglobata” nella form della classe whole e pertanto vengono mostrati direttamente i campi relativi agli attributi di Address in una inner form (una form annidata).

Personalizza il layout a tuo piacere, dando un’occhiata anche alle form di Customer e Address. Il diagramma finale dovrebbe apparire come in figura:

Designer tutorial engine partial

Gestisci l’aggiornamento del modello #

1. Risolvi i problemi di allineamento #

In questa lezione abbiamo apportato moltissime modifiche al modello iniziale. Con buona probabilità abbiamo introdotto dei problemi di allineamento con il database della nostra Cloudlet. Puoi renderti conto di ciò salvando il modello: il Designer ci avverte immediatamente della presenza di high-severity compatibility issues.

Osserva la classe Employee: l’origine del problema è evidenziato in rosso. Poiché il nostro database conteneva in precedenza degli oggetti employee, aggiungendo la composizione con cardinalità minima uno abbiamo invalidato tutti gli oggetti attualmente presenti; nessuno di questi infatti rispetta il vincolo per cui deve essere associato a un oggetto address, visto che la relazione è stata introdotta dopo la loro creazione!

Designer high severity issue employee composition address

Dal Designer puoi cliccare sull’icona della Palette per leggere la issue, che conferma la nostra tesi. Livebase non è in grado di risolvere questo problema, ma possiamo farlo noi muovendoci in due modi: svuotare il database (un database senza record non viola nessun vincolo), oppure rilassare il vincolo (se permettiamo cardinalità minima zero i dati attualmente presenti non violano alcun vincolo).

Optiamo per la seconda opzione: modifica la cardinalità del ruolo Employee.address in Zero or one e salva di nuovo il modello. Come puoi vedere, questa modifica ha risolto il problema e il ruolo whole di Employee è tornato nero come prima.

Chiudi il Designer e accedi al pannello Database di Workforce per consultare le altre issues: l’eliminazione di position e team ha generato due medium severity issues che comportano la perdita dei valori memorizzati. Abbiamo introdotto volutamente questo cambiamento spostando l’informazione nelle nuove classi, pertanto per ora siamo obbligati ad accettarlo.

Questo scenario conferma quanto abbiamo detto in precedenza parlando di lavorare con dati reali: prima di trasferire molti dati su un’applicazione in costruzione, è buona pratica lavorare per un po’ con dati di prova, così da poter testare il funzionamento, accorgersi di eventuali errori o discrepanze con i requisiti di partenza, e infine correggere il modello con facilità (senza rischiare di dover gestire disallineamenti complessi).

Clicca su Resolve all issues e lascia che la piattaforma risolva le due issue sopracitate insieme a tutte quelle di tipo low severity che comporteranno l’aggiunta di nuove tabelle, colonne e relazioni al database.

2. Aggiorna la versione archiviata #

Come di consueto, aggiorna la versione archiviata di TutorialEngine trascinando l’engine dalla Cloudlet e rilasciandolo nell’Engine library; aggiungi il commento che vuoi per questa versione e conferma il salvataggio.

Quando archivi una versione, quelle precedenti dell’engine non vengono sovrascritte; anzi, sono tutte consultabili e disponibili dall’archivio delle versioni. Per aprirlo, fai click destro sul TutorialEngine nella library e seleziona Versions.... Dovresti avere quattro versioni archiviate: 1.0, 2.0, 3.0 e 4.0. Selezionando una versione della lista, possiamo utilizzare i bottoni in basso nel pannello per visualizzare il modello nel Designer (Open), scaricarlo come file .xml (Download), eliminare la versione dalla lista (Discard) oppure visualizzarne i dettagli (View details).

Queste opzioni sono analoghe a tecnologie comuni di version control come Git e ci consentono di trattare i modelli Livebase come codice sorgente, potendo consultare e modificare una particolare versione o ripristinarla in una Cloudlet.

Aggiorna e controlla l’applicazione #

Chiudi il pannello delle versioni di TutorialEngine, avvia Workforce e accedi alla homepage non appena Livebase avrà finito di rigenerare l’applicazione.

Nota come nella vista Application sono comparse le nuove voci di menu Projects, Teams, Qualifications e Customers, ma non quelle per Project_assignment e Address, in quanto classi part!

Apri la Livetable Employees (alla quale mancheranno le colonne Position e Team) e inserisci un nuovo impiegato. Poiché abbiamo rilassato il vincolo, il ruolo Address è rappresentato con una single row table. Se invece proviamo a inserire un nuovo committente da Customers, Address compare come inner form, obbligandoci a creare entrambi gli oggetti.

Prova a compilare tutti i campi di un nuovo oggetto employee e verifica il corretto funzionamento delle cardinalità che abbiamo imposto per le relazioni; per esempio, associare più di tre Qualifications solleverà un data validation error al salvataggio dell’oggetto.

  • Crea delle qualifiche e dei team; assegna poi qualche membro ai team appena creati. Clicca su Add e seleziona qualche record dalla Livetable di selezione che compare come popup. Nota come i record degli impiegati che scegli vengano aggiunti alla lista Members e successivamente non vengano mostrati nella tabella di selezione.
  • Crea un progetto e assegnalo come incarico a un impiegato creando per esso un project_assignment; dopodiché, apri quell’oggetto: nota come aprendo questo oggetto part la scheda sia rimasta la stessa (quella di Employees) e l’incarico venga mostrato nel breadcrumb subito dopo l’impiegato che ne è proprietario.

Gui employee task selection project

La Livetable di selezione di Project durante l’inserimento di un project_assignment

Prenditi qualche minuto per esplorare liberamente l’applicazione e notare come tutti gli aspetti di cui abbiamo parlato finora siano stati effettivamente tradotti in codice funzionante, inclusi i widget, la navigabilità e i vincoli sulle cardinalità delle relazioni.

Conclusioni #

Questa lezione conclude la seconda parte del Tutorial: abbiamo compiuto un enorme passo in avanti nella scoperta di Livebase, trattando l’aspetto chiave delle relazioni tra classi. Concetti come ruoli, cardinalità e widget sono di importanza notevole e spalancano le porte all’apprendimento di argomenti di modellazione avanzati.

Clicca sul bottone per scaricare il modello che abbiamo realizzato in questa lezione:

TutorialEngine_relations.xml

Nella prossima lezione… #

Arricchiremo le relazioni che abbiamo definito nel modello, rendendole più specifiche e in linea con i requisiti;
inoltre, aggiungeremo ulteriori classi e relazioni per completare il modello del database: Modella query e filtri.