CMDBuild Forum

Come avere attributi nelle classi per relazioni 1:1

E' molto comodo poter disporre nel caso di una relazione 1:N della possibilità di creare la relazione "al volo" semplicemente impostando il valore di un attributo della scheda (dal lato N ovviamente).

Sebbene in CMDBuild vi siano i dominii 1:1, non è possibile creare degli attributi con le medesime caratteristiche di praticità.

Per ovviare a questa "mancanza", in seguito a diverse richieste da parte dei miei utenti, ho creato la seguente logica che ad una prima analisi sempra fornire il risultato sperato.

Al momento la stiamo ancora testando quindi usatela in un contesto di produzione a vostro rischio e pericolo...

Ecco gli script da lanciare sul vostro database:

-- Creazione del dominio sulla stessa classe
select * from cm_create_domain('ClasseSelf','CARDIN: 1:1|CLASS1: Classe|CLASS2: Classe|DESCRDIR: connesso a|DESCRINV: connesso aLABEL: Connessione diretta tra oggetti della classe|MASTERDETAIL: false|MODE: reserved|OPENEDROWS: 0|STATUS: active|TYPE: domain')  as result

-- Creazione dell'attributo ricorsivo (da GUI non si può creare!)
select * from cm_create_attribute('"Classe"'::regclass,'Self','int4',NULL,FALSE,FALSE,'BASEDSP: false|CLASSORDER: 0|DESCR: oggetto collegato|FIELDMODE: write|GROUP: |INDEX: 1|MODE: write|REFERENCEDIRECT: false|REFERENCEDOM: ClasseSelf|REFERENCETYPE: restrict|STATUS: active')  as result;

-- Creazione dell'attributo di conteggio ricorsione
select * from cm_create_attribute('"Classe"'::regclass,'RecSeq','int4',NULL,FALSE,FALSE,'BASEDSP: false|CLASSORDER: 0|DESCR: RecSeq|FIELDMODE: hidden|GROUP: |INDEX: 2|MODE: write|STATUS: active')  as result;


-- Imposta il default per i nuovi item che verranno creati
ALTER TABLE "Classe" ALTER COLUMN "RecSeq" SET DEFAULT 0;


-- *** Disabilita i trigger
-- Bonifica valori pre-esistenti
UPDATE "Classe" SET "RecSeq"=0 WHERE "Status"='A';
-- *** Riabilita i trigger

-- Creazione dei trigger per la gestione logica

CREATE OR REPLACE FUNCTION set_data_classe()
  RETURNS trigger AS
$BODY$
  BEGIN
    IF (NEW."RecSeq" <> OLD."RecSeq") THEN
        RETURN NEW;
    END IF;
    IF (NEW."Self" IS NULL) THEN
    -- Cancellazione
        UPDATE "Classe"
        SET "Self"=NULL,"RecSeq"=coalesce("RecSeq",0)+1
        WHERE "Status"='A' AND "Id"=OLD."Self";
        IF ((SELECT "Id" FROM "Map_ClasseSelf" WHERE ("IdObj1"=NEW."Id" OR "IdObj2"=NEW."Id") AND "Status"='A')IS NOT NULL) THEN
            UPDATE "Map_SwitchRouterPortSelf" SET "Status" = 'N' WHERE ("Map_ClasseSelf"."Id" = (SELECT "Id" FROM "Map_ClasseSelf" WHERE ("IdObj1"=NEW."Id" OR "IdObj2"=NEW."Id") AND "Status"='A'));
        END IF;
    ELSE
        -- Update/Inserimento
        UPDATE "Classe"
        SET "Self"=NEW."Id", "RecSeq"=coalesce("RecSeq",0)+1
        WHERE "Status"='A' AND "Id"=NEW."Self";   
        IF ((SELECT "Id" FROM "Map_ClasseSelf" WHERE ("IdObj1"=NEW."Id" OR "IdObj2"=NEW."Id") AND "Status"='A')IS NULL) THEN
            INSERT INTO "Map_ClasseSelf" ("IdClass2","IdClass1","IdObj2","IdDomain","IdObj1","User") VALUES ('"Classe"'::regclass,'"Classe"'::regclass,NEW."Self",'"Map_ClasseSelf"'::regclass,NEW."Id",'admin');   
        END IF;
    END IF;
  RETURN NEW;
  END;
 
$BODY$
  LANGUAGE 'plpgsql' VOLATILE
  COST 100;
ALTER FUNCTION set_data_Classe() OWNER TO utente;

CREATE TRIGGER set_data_classe
  AFTER INSERT OR UPDATE
  ON "Classe"
  FOR EACH ROW
  EXECUTE PROCEDURE set_data_classe();

CREATE OR REPLACE FUNCTION set_data_classeself()
  RETURNS trigger AS
$BODY$
  BEGIN
    IF NEW."Status"='N' THEN
        -- Cancellazione
        UPDATE "Classe"
        SET "Self"=NULL,"RecSeq"=coalesce("RecSeq",0)+1
        WHERE "Status"='A' AND "Id"=NEW."IdObj1";

        UPDATE "Classe"
        SET "Self"=NULL,"RecSeq"=coalesce("RecSeq",0)+1
        WHERE "Status"='A' AND "Id"=NEW."IdObj2";
    ELSE
        -- Creazione
        UPDATE "Classe"
        SET "Self"=NEW."IdObj1","RecSeq"=coalesce("RecSeq",0)+1
        WHERE "Status"='A' AND "Id"=NEW."IdObj2";

        UPDATE "Classe"
        SET "Self"=NEW."IdObj2","RecSeq"=coalesce("RecSeq",0)+1
        WHERE "Status"='A' AND "Id"=NEW."IdObj1";
    END IF;
  RETURN NEW;
  END;
 
$BODY$
  LANGUAGE 'plpgsql' VOLATILE
  COST 100;
ALTER FUNCTION set_data_classeself() OWNER TO utente;
 
 
CREATE TRIGGER set_data_classeself
  BEFORE INSERT OR UPDATE
  ON "Map_ClasseSelf"
  FOR EACH ROW
  EXECUTE PROCEDURE set_data_classeself();
 

Pubblichiamo il contributo di Luca, molto chiaro e ben documentato, sottolineando però due volte la frase "usatela in un contesto di produzione a vostro rischio e pericolo...".

Al di là del fatto che la patch sia ancora in fase di test segnaliamo soprattutto che, intervenendo manualmente sul database di CMDBuild, non c'è nessuna garanzia che:
  • quell'istanza di CMDBuild rimanga poi aggiornabile alle versioni successive tramite il sistema di upgrade automatico disponibile nel sistema
  • la patch rimanga funzionante anche nelle versioni successive (ad esempio nella release 1.4 sono state riviste molte cose proprio nella implementazione delle relazioni) 

 

CMDBuild team