Difference between revisions of "Lianja MetaData API"

From Lianjapedia
Jump to: navigation, search
(Created page with "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 Enterprise RAD platform...")
 
Line 5: Line 5:
 
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.
 
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 we have extended the Lianja App Builder and the Lianja engine to handle metadata with a new 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.
+
To alleviate this problem and to speed up app development we have extended the Lianja App Builder and the Lianja engine to 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.
 
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.
  
We will be adding a MetaData Editor for databases, tables and columns. This will be available soon in the "Data" workspace.
+
In Lianja 3.4 we have added 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.
  
Alternatively, the metadata can be created externally by a CASE/builder tool (such as XCase) and imported into the Lianja App Builder using "Import from XCase..." which will be added to the context menu in the "Data" workspace.
+
This standard metadata format should be used if you want to edit using the MetaData Editor.
  
This will populate (or update) a database with table schemas (native tables or virtual tables) and the associated metadata for the database, tables and columns. Let's now take a look at how this all works. Lianja Databases contain tables and their associated indexes. You can associate metadata with a database.
+
<pre>// Create an object from a metadata string
 +
obj = metadata_decode("name=barry;company=Lianja;amount=10")</pre>
 +
<pre>// Encode a string from an object representing metadata
 +
str = metadata_encode(obj)</pre>
 +
Tip: When specifying MetaData names they can be in any format you want:
  
<pre>create database mydb metadata "json encoded string"
+
<pre>this.is.a.name=value</pre>
 +
 
 +
The MetaData names can also be applied conditionally (note how [[OData Operators|OData expression syntax]] is used):
 +
 
 +
<pre>section.backcolor[amount lt 1000]=red
 +
section.backcolor[amount ge 1000]=lightgreen</pre>
 +
 
 +
The "true" and "false" selections can be combined:
 +
 
 +
<pre>section.backcolor[amount lt 1000]=red,lightgreen</pre>
 +
 
 +
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.
 +
 
 +
<pre>formitem.backcolor[amount lt 1000]=red</pre>
 +
 
 +
Note that Lianja 3.4 introduced a new Lianja system object method [[Lianja|Lianja.applyMetaData()]], and also added this method to page, section and formitem objects returned from Lianja.getElementByID(). e.g.
 +
 
 +
<pre>Lianja.getElementByID("page1.section1").applyMetaData()</pre>
 +
 
 +
This is useful when called in a changed delegate to adjust the appearance of a section after editing a UI control.
 +
 
 +
Let's now take a look at how this all works.
 +
 
 +
Lianja Databases contain tables and their associated indexes. You can associate metadata with a database.
 +
 
 +
<pre>create database mydb metadata "metadata_string"
 
open database mydb
 
open database mydb
 
omd = databaseMetaData()</pre>
 
omd = databaseMetaData()</pre>
Line 63: Line 94:
 
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. To provide a flexible solution so that any type of user defined metadata can be associated with a table you use the METADATA clause on the [[CREATE TABLE|CREATE]] or [[ALTER TABLE|ALTER]] table SQL DDL commands.
 
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. To provide a flexible solution so that any type of user defined metadata can be associated with a table you use the METADATA clause on the [[CREATE TABLE|CREATE]] or [[ALTER TABLE|ALTER]] table SQL DDL commands.
  
The metadata you specify should be a JSON encoded character string. After a table is opened you can obtain a metadata object using the new [[TABLEMETADATA()|tableMetaData()]] function. Example:
+
The metadata you specify should be a standard metadata encoded character string. After a table is opened you can obtain a metadata object using the new [[TABLEMETADATA()]] function. Example:
  
 
<pre>use customers  
 
<pre>use customers  
omd = json_decode(tableMetaData())</pre>
+
omd = metadata_decode(tableMetaData())</pre>
  
The [[TABLEMETADATA()|tableMetaData()]] function returns the METADATA string from the Active Data Dictionary associated with the table. This can be a JSON encoded complex object containing properties that are arrays of objects or nested objects. You can change the elements of the metadata object then update the Active Data Dictionary with the new metadata. Example:
+
The [[TABLEMETADATA()]] function returns the METADATA string from the Active Data Dictionary associated with the table. You can change the elements of the metadata object then update the Active Data Dictionary with the new metadata. Example:
  
 
<pre>use customers
 
<pre>use customers
omd = json_decode(tableMetaData())
+
omd = metadata_decode(tableMetaData())
 
omd.searchpanelvisible = .t.
 
omd.searchpanelvisible = .t.
 
use
 
use
alter table customers metadata json_encode(omd)</pre>
+
alter table customers metadata metadata_decode(omd)</pre>
  
 
It is important to realize that the members of the metadata object are completely up 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.
 
It is important to realize that the members of the metadata object are completely up 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.
  
Metadata can also be associated with columns of a table. You use the [[COLUMNMETADATA()|columnMetaData(cExp)]] function to obtain the column metadata. Example:
+
Metadata can also be associated with columns of a table. You use the [[COLUMNMETADATA()]] function to obtain the column metadata. Example:
  
 
<pre>omd = object()
 
<pre>omd = object()
 
omd.caption = "Customer Name"
 
omd.caption = "Customer Name"
 
omd.searchfield = .t.
 
omd.searchfield = .t.
alter table customers modify constraint name metadata json_encode(omd)
+
alter table customers modify constraint name metadata metadata_decode(omd)
 
use customers
 
use customers
omd = json_decode( columnMetaData("name") )
+
omd = metadata_decode(columnMetaData("name"))
 
omd.caption = "Customer"
 
omd.caption = "Customer"
 
omd.picture = "@!"
 
omd.picture = "@!"
 
omd.searchfield = .t.
 
omd.searchfield = .t.
alter table customers modify constraint name metadata json_encode(omd)</pre>
+
alter table customers modify constraint name metadata metadata_decode(omd)</pre>
  
 
How does this all work with the Lianja App Builder?
 
How does this all work with the Lianja App Builder?
Line 97: Line 128:
 
<pre>// File: database:setupUI_tablename.prg then app:setupUI.prg
 
<pre>// File: database:setupUI_tablename.prg then app:setupUI.prg
 
parameter id
 
parameter id
local osection = Lianja.getElementByID( id )
+
local osection = Lianja.getElementByID(id)
  
 
// check to see if UI section attributes are same version as last time applied
 
// check to see if UI section attributes are same version as last time applied
Line 105: Line 136:
 
endif
 
endif
 
osection.metaDataVersion = tableMetaDataVersion()
 
osection.metaDataVersion = tableMetaDataVersion()
local omd = json_decode( tableMetaData() )
+
local omd = metadata_decode(tableMetaData())
  
 
// Call setupUI to override deferred page loading when applying the MetaData
 
// Call setupUI to override deferred page loading when applying the MetaData
Line 117: Line 148:
 
local count = osection.count
 
local count = osection.count
 
for i=1 to count
 
for i=1 to count
   oitem = osection.item( i )
+
   oitem = osection.item(i)
   omd = json_decode( columnMetaData( oitem.controlsource ) )
+
   omd = metadata_decode(columnMetaData( oitem.controlsource ))
 
   if not isObject(omd)
 
   if not isObject(omd)
 
     loop
 
     loop

Revision as of 07:42, 27 February 2017

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 Enterprise RAD 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 we have extended the Lianja App Builder and the Lianja engine to 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.

In Lianja 3.4 we have added 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.

This standard metadata format should be used if you want to edit using the MetaData Editor.

// 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)

Tip: 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

Note that 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.

Let's now take a look at how this all works.

Lianja Databases contain tables and their associated indexes. You can associate metadata with a database.

create database mydb metadata "metadata_string"
open database mydb
omd = databaseMetaData()

Each table in a Lianja database can have an associated active data dictionary 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. To provide a flexible solution so that any type of user defined metadata can be associated with a table you use the METADATA clause on the CREATE or ALTER table SQL DDL commands.

The metadata you specify should be a standard metadata encoded character string. After a table is opened you can obtain a metadata object using the new TABLEMETADATA() function. Example:

use customers 
omd = metadata_decode(tableMetaData())

The TABLEMETADATA() function returns the METADATA string from the Active Data Dictionary associated with the table. You can change the elements of the metadata object then update the Active Data Dictionary with the new metadata. Example:

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

It is important to realize that the members of the metadata object are completely up 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.

Metadata can also be associated with columns of a table. You use the COLUMNMETADATA() function to obtain the column metadata. Example:

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

How does this all work with the Lianja App Builder?

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. Example:

// 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 isObject(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 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.