EDO

Data Manipulation

Detailing the markups used to access and manipulate data resources.

Introduction

Once data sources and records are defined, the data associated with them can be manipulated using a further set of markups.

FOREACH

The <FOREACH> markup is similar to an SQL SELECT query (it isn't called <SELECT>, as there is already a standard HTML markup called that). It reads multiple records from a data source, and can do something for each record. The options available to it mostly correlate to normal SELECT parameters. These include:

RECORD (required)
This defines what record to use to store each row of data while processing it. If a record does not contain all the fields from the views/tables being queried, then those fields are ignored. Likewise, queried fields need to have names matching the record variables to be stored in the correct record field.
VIEW (required)
This defines what view (or views) to retrieve the data from. If more than one view is specified (comma-separated), then a relation/join between the views should be defined in the WHERE or JOIN options. Views can also be aliased to another name for use elsewhere in the query, using the syntax "viewname as viewalias".
FIELDS (optional)
This optional parameter lists (comma-separated) what fields to retrieve from the view(s), to store in the record. If this is undefined, then all matching field names will be copied. Fields with different names between the view and record can be renamed using the syntax "viewfield as recordfield". This syntax can also be used to include SQL function calls on the queried columns, storing the result in the right record field.
WHERE (optional)
This contains an optional condition to filter the data. Although this can also contain a condition for joining multiple views, that is preferred to be defined in the JOIN parameter.
JOIN (recommended for multiple views)
This is similar to WHERE, but should specifically contain the conditions defining the relationship between multiple views. This is useful, because for some advanced forms of <FOREACH>, it is necessary to treat the row filter separately to the view relationships. (See examples later on for more information.)
MATCH (optional)
This is an alternate condition-matching method. When one or more fields are listed (comma-delimited) in the MATCH parameter, the query only matches those rows whose values of these fields exactly match the values already stored in the target record.
SORT (optional)
Defines what field(s) to sort the queried rows by.
LIMIT (optional)
Similar to some SQL databases, this parameter allows the number of iterations to be limited to a certain number. (Currently only implemented for DBs which natively support the LIMIT SQL Select parameter.)
GROUP (optional)
Group rows together by a field name with the same values across rows. Used in SQL Queries for use with group-related field functions.

In addition to the <FOREACH> parameters, there are a number of custom markups which may be used within a Foreach block:

<PRELIST>
Any content within this markup is only shown before the main list of content. If there is no content (ie; no rows matching), then this is not shown at all.
<POSTLIST>
Any content within this markup is only shown after the main list of content. If there is no content (ie; no rows matching), then this is not shown at all.
<NOLIST> (Currently not implemented!)
Any content within this markup is only shown if there is no content (ie; no rows matching).

ie; To show a listing of multiple records, any HTML within a <PRELIST> comes before the list, and any HTML within a <POSTLIST> comes after the list, but only if that query returns at least one matching row. If there are no matches at all, then the HTML within the <NOLIST> will be shown.

Here's some examples of Foreach queries:

Selecting all fields from View "people" into Record "person", ordered by field "surname", displaying the "firstname" and "surname" in a list:
<FOREACH RECORD="person" VIEW="people" SORT="surname">
   $person.firstname $person.surname<BR>
</FOREACH>
Selecting fields "firstname" and "surname" into Record "person" from View "people", where the field "surname" matches the value already stored in the record:
<FOREACH RECORD="person" VIEW="people" MATCH="surname">
   $person.firstname $person.surname<BR>
</FOREACH>
Selecting all fields from View "people" into Record "person", ordered by field "surname", displaying the "firstname" and "surname" in a table, with 2 columns and appropriate headers, if there are matches:
<FOREACH RECORD="person" VIEW="people" SORT="surname">
 <PRELIST>
   <TABLE>
   <TR> <TH>First Name</TH> <TH>Surname</TH> </TR>
 </PRELIST>
   <TR> <TH>$person.firstname</TH> <TH>$person.surname</TH> </TR>
 <POSTLIST>
   </TABLE>
 </POSTLIST>
</FOREACH>
Selecting fields "firstname" and "surname" into Record "person" from Views "people" and "status" where field "current" in "status" equals "Y". The two views both contain a field "ID" and are joined on this field. The matching names are sorted by "surname", and listed in a bulleted list:
<FOREACH RECORD="person" VIEW="people,status" 
   FIELDS="firstname,surname" SORT="surname"
   WHERE="status.current='Y'" JOIN="people.ID=status.ID">
 <PRELIST>
   <UL>
 </PRELIST>
   <LI>$person.firstname $person.surname</LI>
 <POSTLIST>
   </UL>
 </POSTLIST>
</FOREACH>

IMPORT

The <IMPORT> markup is used to import data from a data source (specified by a View) or from another record. It is similar in many ways to the <FOREACH> markup, since it supports most of the same arguments. The primary differences are:

Compared to <FOREACH>, <IMPORT> supports the following equivalent properties: RECORD, VIEW, FIELDS, WHERE, JOIN, MATCH, SORT.

The <IMPORT> markup also supports the following specific properties:

FROMRECORD
The name of the record to Import from. Used when copying multiple values from one record to another, as opposed to importing from another type of data source.

Here's some examples of Import queries:

Import all matching fields into Record "formdata" from the special view "CGI" (ie; populate "formdata" with data submitted as a CGI):
<IMPORT RECORD="formdata" VIEW="CGI">
Only import the fields "date", "title", and "notes" into Record "formdata" from the special view "CGI":
<IMPORT RECORD="formdata" FIELDS="date,title,notes" VIEW="CGI">
Import all matching fields into Record "formdata" from the record "olddata":
<IMPORT RECORD="formdata" FROMRECORD="olddata">

EXPORT

The <EXPORT> markup is used to export data from a Record, to a View (typically an SQL Table). It functionally fills the role of the SQL UPDATE and INSERT commands, determining which to use via the presence or absence of a condition-style parameter. ie; if a MATCH or WHERE parameter is specified (similar syntax as in <FOREACH>), then it updates existing matching records, otherwise it inserts a new record. it inserts a new record.

An optional FIELDS parameter can be set to export a selection of fields. If not set, the default behaviour is to export all fields that exist in both the record and the view.

If a condition (MATCH/WHERE) is specified, but the update fails, then an error message will be generated. If the desired behaviour is such that if no existing record is found, a new record is inserted, then the additional property ONERROR can be set to "insert". Warning: This behaviour is largely untested, and may change in the future!

Here's some examples of Export queries:

Export all fields from the Record "formdata" as a new row in View "data":
<EXPORT RECORD="formdata" VIEW="data">
Export fields "firstname" and "surname" from the Record "formdata" to the View "people", where the field "ID" matches the current value in the record "ID" field:
<EXPORT RECORD="formdata" VIEW="people" MATCH="ID">

DELETE

The <DELETE> markup is used to delete data from a View. It uses fields from an optionally specified RECORD in conjunction with a MATCH or WHERE condition, to specify which rows of data in a View to delete. Is the RECORD really Optional?

Here's some examples of Delete statements:

Delete from View "data" all rows whose fields "group" and "section" match those stored in a Record "org":
<DELETE WHERE="group='$org.group' and section='$org.group'">
The same example, but using a defined RECORD and MATCH:
<DELETE RECORD="org" MATCH="group,section">


ASSIGN

The <ASSIGN> markup is another way of getting information into Record Fields, but does it one Field at a time; it's a way of assigning a value to a single field. Parameters include:

RECORD (optional)
This defines which record the field is in to assign the value to. If this is not specified, the default Record is assumed. A record can also be specified in the FIELD parameter.
FIELD (required)
This defines which field is to be updated to the new value. If the RECORD parameter is not used, a record can still be specified as part of the Field specifier, using the notation: record.field
VALUE (required if EVAL is not specified)
This specifies the content of the value to assign to the field.
EVAL (required if VALUE is not specified)
This specifies an SQL expression to evaluate, with the resulting value being saved as the value to assign to the field. This currently requires a SOURCE to be specified, as that source is used to evaluate the expression. As such, built-in functions of the source can be leveraged for complex calculations.
SOURCE (required if EVAL is used)
The SQL source to use to evaluate an expression.

Here's some examples of Assign statements:

Assign to the field "count" the value of "0", in the default Record:
<ASSIGN FIELD="count" VALUE="0">
Assign to the field "count" the result of evaluating the expression "6*8", in the Record "track", using the Source "data":
<ASSIGN RECORD="track" FIELD="count" SOURCE="data" EVAL="6*8">
Increment the value of the field "count" by 1, in the Record "track", using the Source "data":
<ASSIGN FIELD="track.count" SOURCE="data" EVAL="$track.count.value+1">
($track.count.value means insert the "value" of field "count" from record "track")

CLEAR

The <CLEAR> markup is used to clear the values of one or more fields in a record. Parameters include:

RECORD (required)
This defines which record contains the fields to clear. Warning: This currently does not default to the default Record, and so is currently a required parameter.
FIELDS (optional)
This specifies which fields within the record to clear. This parameter is optional, and if not specified, all fields within the record will be cleared.