Skip to content

Data Modelling: Extending Data Models

Julio Carneiro edited this page Nov 30, 2017 · 4 revisions

4D table Data Models can be extended, for example, to add related fields to a record definition. Or to add calculated values, or lists of subordinate records, or even custom properties.

Related Fields Example

Here is an example of a ShellUsers class extension, that adds a field from a related one table, Location:

import { ShellUsers } from '../DB/ShellUsers';
import { Location } from '../DB/Location';

export class ShellUsersEx extends ShellUsers {
    fields: Array<any> = [
        { name: 'LocationName', longname: Location.kLocationName, type: 'text', related: true }
   ].concat(new ShellUsers().fields);

    // related fields

    get LocationName(): string { return this.get('LocationName'); }
    set LocationName(v: string) { this.set('LocationName', v); }

 
}

On 4D side, there must be a relation defined between ShellUsers and Location. The FourDModel class, by way of the RESTApi 4D component, will resolve that relation when retrieving records using this ShellUsersEx class.

Subordinate Record List Example

This is an example where we want to retrieve a record along with subordinate, related many, records.

import { TasteProfiles } from '../DB/TasteProfiles';
import { ShellUsers } from '../DB/ShellUsers';
import { ProfileGenes } from '../DB/ProfileGenes';
import { ProfileGenesEx } from './ProfileGenesEx';
import { ViewerContent } from '../DB/ViewerContent';
import { ViewerContentEx } from './ViewerContentEx';

export class TasteProfilesEx extends TasteProfiles {
    fields: Array<any> = [
        { name: 'UserName', longname: ShellUsers.kUserName, type: 'text', related: true },
        { name: 'profileGenesList', subTable: new ProfileGenesEx(), joinFK: ProfileGenes.kProfileID, joinPK: TasteProfiles.kProfileID },
        { name: 'viewerContentList', subTable: new ViewerContentEx(), joinFK: ViewerContent.kProfileID, joinPK: TasteProfiles.kProfileID }
   ].concat(new TasteProfiles().fields);

    // related fields

    get UserName(): string { return this.get('UserName'); }
    set UserName(v: string) { this.set('UserName', v); }

    // children records
    get profileGenesList(): Array<ProfileGenesEx> { return this.get('profileGenesList'); }
    set profileGenesList(v: Array<ProfileGenesEx>) { this.set('profileGenesList', v); }

    get viewerContentList(): Array<ViewerContentEx> { return this.get('viewerContentList'); }
    set viewerContentList(v: Array<ViewerContentEx>) { this.set('viewerContentList', v); }

}

In this example, we want to retrieve records from 2 related many tables. The subTable property indicates the Data Model that corresponds to the related many table. The joinFK and joinPK properties define the fields on each side that are used to retrieve the subordinate records. The fields to be retrieved from those related tables are defined by the subTable Data Model.

A More Complex Example

Here a case that includes examples of calculates values and custom properties:

import {SVODWindows} from '../DB/SVODWindows';
import {Filmes} from '../DB/Filmes';

export class SVODWindowsEx extends SVODWindows {
    public fields:Array<any> =  [
        {name: 'ProgramTitle', longname: Filmes.kTituloContrato, type: 'text', related:true},
        {name: 'PublishRequestStatus', formula: 'SVODGetPublishRequestStatus', type: 'text'},
        {name: 'ChannelSynopsis', formula: 'UTchGetChannelSynopsis([SVODWindows]Channel)', type: 'text'},
        {name: 'ChannelProgramSet', formula: 'UTchGetChannelProgramSet([SVODWindows]Channel)', type: 'text'},
        {name: 'HasAncillarySchedule', formula: 'UTILBooleanToText (UTILdbsisRecordAlreadyInDB (-&gt;[SVODAncillaryEvent]OfferID;-&gt;[SVODWindows]RecordID);&quot;Yes&quot;;&quot;No&quot;)', type: 'text'}
     ].concat(new SVODWindows().fields);

	get ProgramTitle ():string {return this.get('ProgramTitle');}
	set ProgramTitle (v:string) {this.set('ProgramTitle',v);}

    set PublishRequestStatus (v:string) {this.set('PublishRequestStatus',v);}
    get PublishRequestStatus ():string {return this.get('PublishRequestStatus');}

    set ChannelSynopsis (v:string) {this.set('ChannelSynopsis',v);}
    get ChannelSynopsis ():string {return this.get('ChannelSynopsis');}

    set ChannelProgramSet (v:string) {this.set('ChannelProgramSet',v);}
    get ChannelProgramSet ():string {return this.get('ChannelProgramSet');}
    
    set HasAncillarySchdule (v:string) {this.set('HasAncillarySchdule',v);}
    get HasAncillarySchdule ():string {return this.get('HasAncillarySchdule');}

    get eventType():string {
        if (this.ProgramID && this.ProgramID !== '') return 'Program';
        if (this.PromoHN && this.PromoHN !== '') return 'Promo';
        if (this.ProductHN && this.ProductHN !== '') return 'Interstitial';
        if (this.CommercialHN && this.CommercialHN !== '') return 'Commercial';
        
        return '';
    }

}

Here we have calculated fields, whose content is determined via calls to 4D methods, when retrieving the record, by ways of the formula property in the field declaration.

The example also includes a custom property, eventType, that is calculated from the contents of other fields in the record.