Lianja MetaData API

From Lianjapedia
Jump to: navigation, search

Introduction

In the beginning there is data. Without data we have nothing to build LOB (Line Of Business) Apps against.

The primary goal of Lianja is to provide an No-Code / Low-Code platform for Desktop, Web and Mobile Apps.

When building large enterprise class applications with 100s of tables that are used in multiple Apps and their associated Lianja UI elements, it can be tedious and error prone to apply the same section, field and grid column attributes consistently throughout the UI using the attributes panel in the App Builder.

To alleviate this problem and to speed up app development the Lianja App Builder and the Lianja engine handle metadata with a MetaData API. This mechanism provides a way of dynamically setting up UI element attributes when a new section is created or when an app is loaded and its pages and their corresponding sections are loaded.

Also, and probably more importantly, any changes to the metadata for the tables and/or their columns contained in the database schema that are used in many different Lianja apps and their UI elements (Sections, Fields, Grid Columns) are propagated throughout apps dynamically as they are loaded at design time.

MetaData Format

Lianja 3.4 introduced a MetaData Editor for databases, tables and columns. This can be activated by clicking the edit icon in the database, table or columns panel in the "Data" workspace.

The following standard metadata format of semi-colon separated name=value pairs with no internal quotes should be used if you want to edit using the MetaData Editor.

"name1=value1;name2=value2;name3=value3"

e.g.

cMetadata = "name=barry;company=Lianja;amount=10"

The METADATA_DECODE() and METADATA_ENCODE() functions can be used to create an object from a metadata string or create a metadata string from an object with metadata members respectively.

// Create an object from a metadata string
obj = metadata_decode("name=barry;company=Lianja;amount=10")
// Encode a string from an object representing metadata
str = metadata_encode(obj)

When specifying MetaData names they can be in any format you want:

this.is.a.name=value

The MetaData names can also be applied conditionally (note how OData expression syntax is used):

section.backcolor=[amount lt 1000]red
section.backcolor=[amount ge 1000]lightgreen

The "true" and "false" selections can be combined:

section.backcolor=[amount lt 1000]red,lightgreen

When data is refreshed in a section, the table metadata is read and if any conditional metadata exists it is evaluated and applied to the section.

The same procedure is then performed on formitems (column MetaData is read) which should be in this format.

formitem.backcolor=[amount lt 1000]red

Database Metadata

Lianja Databases contain tables and their associated indexes. You can associate metadata with a database. The metadata is store in the database's <basename>.cad file.

create database mydb metadata "metadata_string"

DATABASEMETADATA()

The DATABASEMETADATA() function returns the database metadata as a string. This can be converted to an object using the METADATA_DECODE() function.

open database mydb
omd = metadata_decode(databaseMetaData())

Table Metadata

Each table in a Lianja database can have an associated active data dictionary (<table-basename>.dbd) which contains business rules and triggers for the table itself and its columns. You manipulate these using standard SQL DDL Column constraints:

alter table customers modify constraint name set default "Barry"
alter table customers modify constraint name drop default

alter table customers modify constraint name set check not empty(name)
alter table customers modify constraint name drop check

alter table customers modify constraint name set error "Name cannot be blank"
alter table customers modify constraint name drop error

alter table customers modify constraint name set picture "@!"
alter table customers modify constraint name drop picture   

alter table customers modify constraint name set choices "Apples,Oranges,Bananas"
alter table customers modify constraint name drop choices

alter table customers modify constraint name set not null
alter table customers modify constraint name set null  

alter table customers modify constraint pkid set autoinc
alter table customers modify constraint pkid drop autoinc   

alter table customers modify constraint name set help "Customer name"
alter table customers modify constraint name drop help

Triggers (written in Lianja/VFP):

alter table customers modify onopen "customers_onopen"    
alter table customers modify onclose "customers_onclose"    
alter table customers modify oninsert "customers_oninsert"    
alter table customers modify onbeforeinsert "customers_onbeforeinsert"    
alter table customers modify onafterinsert "customers_onafterinsert"    
alter table customers modify onupdate "customers_onupdate"    
alter table customers modify onbeforeupdate "customers_onbeforeupdate"    
alter table customers modify onafterupdate "customers_onafterupdate"    
alter table customers modify ondelete "customers_ondelete"    
alter table customers modify onbeforedelete "customers_onbeforedelete"    
alter table customers modify onafterdelete "customers_onafterdelete"

We call this an "Active Data Dictionary" as these business rules are associated with the table whenever it is used in a desktop App, Lianja SQL Server or Lianja Cloud Server. Altering any of the attributes has immediate effect on all applications that use the corresponding table without needing to make any code changes. The Active Data Dictionary can also be associated and used with Virtual Tables (third party databases e.g MSSQL, MySQL, PostgreSQL etc) when the tables are opened.

The Active Data Dictionary can now also store any type of user defined table and column metadata. You use the METADATA clause on the CREATE TABLE or ALTER TABLE SQL commands to define the table metadata.

The metadata you specify should be a standard metadata encoded character string.

TABLEMETADATA()

The TABLEMETADATA() function returns the metadata from an open table as a string. This can be converted to an object using the METADATA_DECODE() function.

use customers 
omd = metadata_decode(tableMetaData())

You can change the elements of the metadata object then update the Active Data Dictionary with the new metadata.

use customers
omd = metadata_decode(tableMetaData())
omd.searchpanelvisible = .t.
use
alter table customers metadata metadata_encode(omd)

It is important to realize that the members of the metadata object are defined completely by the data architect who creates the database schema. This provides them with the ability to create a complex relationship of database tables and their associated UI attributes in a database design tool such as XCase or other similar type of ER CASE tool.

Column Metadata

Metadata can also be associated with columns of a table and is stored in the table's Active Data Dictionary.

COLUMNMETADATA()

You use the COLUMNMETADATA() function to obtain the column metadata for the specified column in the currently active table as a string. This can be converted to an object using the METADATA_DECODE() function.

omd = object()
omd.caption = "Customer Name"
omd.searchfield = .t.
alter table customers modify constraint name metadata metadata_encode(omd)
use customers
omd = metadata_decode(columnMetaData("name"))
omd.caption = "Customer"
omd.picture = "@!"
omd.searchfield = .t.
alter table customers modify constraint name metadata metadata_encode(omd)

How does this all work with the Lianja App Builder?

From Lianja v3.4, Table and Column MetaData attributes defined with 'section.' and 'formitem.' prefixes will be automatically applied to Sections and Formitems (in Form and Canvas Sections).

Custom MetaData can be applied in a setupUI.prg script as described below.

When you create a new section by dragging a table onto a page (or opening an existing App) Lianja will layout the section, then if the script setupUI.prg exists in the App or setupUI_tablename exists in the database it will be called with the section id as the first parameter and the cursor for the table bound to that section will be current.

The TABLEMETADATAVERSION(), incremented each time the ALTER TABLE ... METADATA "..." is executed, can be compared to the Section MetaData version to prevent MetaData being applied multiple times.

// File: database:setupUI_tablename.prg then app:setupUI.prg
parameter id
local osection = Lianja.getElementByID(id)

// check to see if UI section attributes are same version as last time applied
// and if so do nothing (improves performance)
if osection.metaDataVersion = tableMetaDataVersion()
    return
endif
osection.metaDataVersion = tableMetaDataVersion()
local omd = metadata_decode(tableMetaData())

// Call setupUI to override deferred page loading when applying the MetaData
osection.setupUI()

// Interrogate the metadata and apply the attributes to the section
// ...
// foreach formitem in the form section (can also traverse grid columns)
local i
local oitem
local count = osection.count
for i=1 to count
  oitem = osection.item(i)
  omd = metadata_decode(columnMetaData(oitem.controlsource))
  if not is_object(omd)
    loop
  endif
  // Interrogate the column metadata and apply the attributes to the field or grid column
  // ...
endfor

If your App is a Web or Mobile App you will now need to "Preview" it and then "Deploy" it (or rebuild a Mobile App) to reflect the changes made in the metadata.

To set the attributes for a page, section or form item use the setAttr(name, value) method which has been added to each of these objects.

For all attribute names that can be set on pages, sections and formitems, follow the links here.

applyMetaData()

Lianja 3.4 introduced a new Lianja system object method Lianja.applyMetaData(), and also added this method to page, section and formitem objects returned from Lianja.getElementByID(). e.g.

Lianja.getElementByID("page1.section1").applyMetaData()

This is useful when called in a changed delegate to adjust the appearance of a section after editing a UI control.