Sigfrid
Lundberg´s
Stuff

A form with a join

Sigfrid Lundberg's Stuff 2010-03-05

Bookmark and Share

A form is a document, or a part of one, that can be used for the entry of data. A join is a construct in query languages such SQL. A join allows you to lookup data in one part of a database based on a query in an other part. Joins are general, and may appear in other contexts than SQL. Such as XQuery, XSLT and XML forms language, XForms. I've recently written my first extensive application in that language. It required a larger effort than I had expected.

Having worked with XML processing for more than ten years, I had thought that I would easily be able to relate to a new XML technology by extrapolating from my earlier experiences. This has hitherto been the case. For instance, learning Xerces in java when I've used dom4j in java and XML::LibXML in perl was a piece of cake. If you have used the venerable Expat callback based parser, the idea behind SAX or Stream API for XML (StaX) are quite obvious.

One could expect that if you know XML technologies and HTML forms you would easily grasp XForms. Having realised that this wasn't the case I thought that having learned XPath and XSLT I would easily grasp XForms. That was true, but only partly. A small part.

UI and Events

An XForm script, just as an XSLT one, can read XML documents and act upon them. The result is very different. XSLT generates another document, usually an XML one. The XForm generates a form, a graphical user interface. And it is usually one that can be used for editing XML. A user interface is event driven, an there are a whole lot of events to keep track of.

<data> 
  <lookup>
    <values xml:id="id1">
      <value>one a</value>
      <value>one b</value>
      <value>one c</value> 
    </values>
    <values xml:id="id2"> 
      <value>two a</value>
      <value>two b</value> 
      <value>two c</value>
    </values> 
    <values xml:id="id3">
      <value>three</value> 
    </values>
  </lookup> 
  <keys> 
    <key lookup="id1">first</key> 
    <key lookup="id2">second</key>
    <key lookup="id3">third</key>
  </keys> 
</data>
      

Fig. 1. An XML snippet where there is a list of keys that via a reference (a so-called IDREF) in an attribute called lookup refer to nodes in another part of the document. The references are anchored using xml:id attributes. The relation between the keys and the values is one to many.

What is a brilliant feature in XSLT might not work at all in a GUI, so if you're a lover of the functional programming style recursive processing in XSLT you'll be disappointed. XForms isn't XML transformed into forms, it is language for writing GUIs for XML.

Typically one can write really nifty GUIs in XForms. You'll find a lot of examples online, for instance by following links from the Wikipedia article. There are various implementations, server side ones as well as those running client side. I opted for the one implemented as a Firefox plug-in.

Joins and XML documents

My project is about editing quite complicated documents, namely really heavy beasts in Music Encoding Initiative XML. We are building a MEI application while the inititive are revising the specification and, among other things, move from a DTD to RelaxNG. A wise move.

I might return to the project itself at a later stage, but here I want to tell you about XForms itself. And about joins. Consider the fragment in Fig. 1. If you want to be able to edit the values in the vicinity of the keys, you may need a form like this (requires XForms in your browser). The essential code performing the join can be studied in Fig. 2.

      <xf:group ref="instance('data-instance')/keys">
          <xf:repeat nodeset="key"
        	  id="lookup-keys-loop">
               <!-- do things with each key -->
               <xf:repeat 
                       nodeset="instance('data-instance')/lookup/
                       values[@xml:id = current()/@lookup]/value"
                       id="value-loop">
               <!-- do things with value group -->
               </xf:repeat>
          </xf:repeat>
       </xf:group>
	

Fig. 2. XForms snippet that loops around all key elements and for each of them make the lookup. That is, here we have the join in red colour. xf is short for the XForms namespace, which is http://www.w3.org/2002/xforms. Note that this code works inside a single document that looks like the one in Fig. 1. You cannot edit a database this way.

   select
      last_name,
      department_name
   from
      employees e 
   left outer join 
      departments d
   on
      e.department_id=d.department_id;
	 

Fig. 3. The SQL equivalent to the XForms code in Fig. 2. The SQL assumes that there is a table called employees and another called departments, and that employees table contains department_id as a foreign key. This setup is similar with the key-values (based on IDREF & ID) arrangements in Fig. 1-2.

The core is the XPath function current() which returns the current context node. This construct can then be combined with input fields, text areas etc. The corresponding SQL code could look as in Fig. 3.

Get the last inserted ID

It's the same old story, but it's new to me. Whenever you use a RDBMS, you will sooner or later ask the question: Is there a way to get the last inserted ID? This, if any, is indeed a SQL FAQ.

That is, you'll inserted a line in a table, and now you want to use this ID as a foreign key in another table. That is you want to create data that can be retrieved using a join. All SQL dialects and APIs provide facilities for this.

Unfortunately, you don't have such functions in neither XForms nor XSLT. In XSLT that is no big deal; you can program anything in that language. It is worse in XForms. Fig. 4 shows shows an XForms trigger that executes a number of actions. It inserts two nodes, one containing an IDREF and the other the corresponding ID (like inserting both a department and an employee in the SQL setup in Fig. 3).

The IDREF/ID values are created on the fly using XPath functions. The problem turned out to be to get hold of the value again for the second insert.

<xf:trigger>
  <xf:label>Add key and value</xf:label>
  <xf:action ev:event="DOMActivate">
    <xf:insert nodeset="key"
	 at="last()"
	 position="after" 
	 origin="instance('empty-instance')/keys/key"/>
    <xf:setvalue ev:event="DOMActivate"
	 ref="key[last()]/@lookup" 
	 value="concat('id',digest(string(random(true)), 'MD5', 'hex'))"/>
    <xf:insert nodeset="instance('data-instance')/lookup/values"
	 at="last()" 
	 position="after" 
	 origin="instance('empty-instance')/lookup/values"/>
    <xf:setvalue ev:event="DOMActivate"
	 ref="instance('data-instance')/lookup/values[last()]/@xml:id"
	 value="instance('data-instance')/keys/key[last()]/@lookup"/>
  </xf:action>
</xf:trigger>
	 

Fig. 4. XForms code that inserts two elements in a document of the kind shown in Fig. 1. The key element contains an IDREF (the attribute lookup) which points the corresponding xml:id among the values. The form operates by copying nodes from an empty instance and inserting them into the data-instance, i.e., the instance being edited by the user. The last setvalue contains the solution to the SQL FAQ: How do I get the last inserted ID

Take home message

I've learned a lot of XForms and some new XPath functions. The code I needed in the Music Encoding initiative example was much more complicated than the trigger in Fig. 4.

The very reason for writing this was that I used more than a week on this join and the corresponding ID/IDREF thing. I hadn't expected that complexity.

But having been through this, I don't think there is any format that cannot be edited using XForms 1.1. It is a pity that virtually all text books and tutorials out there is XForms 1.0 and six or seven years old. This is a very good technology that deserves a breakthrough.

Home

Subscribe to Stuff from Sigfrid LundbergSubscribe to my stuff

stuff by category || year

NB

My name is Sigfrid Lundberg. The stuff I publish here may, or may not, be of interest for anyone else.

On this site there is material on photography, music, literature and other stuff I enjoy in life. However, most of it is related to my profession as an Internet programmer and software developer within the area of digital libraries. I have been that at the Royal Danish Library, Copenhagen (Denmark) and, before that, Lund university library (Sweden).

The content here does not reflect the views of my employers. They are now all past employers, since I retired 1 May 2023.

Creative Commons License
This entry (A form with a join) within Sigfrid Lundberg's Stuff, by Sigfrid Lundberg is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.