<!-- $Id: 05-tpl_form.sgml,v 1.2 2001/08/15 22:37:59 richardarcher Exp $ -->
<sect1>tpl_form

<p> The <tt/tpl_form/ class is intended to provide a general framework for
HTML form deployment. It heavily depends on <tt/OOH Forms/ library, so it
is required that you read and understand the relative documentation.

<p>The main idea is that you create a black box by sub-classing 
<tt/tpl_form/, provide some HTML code mixed with <tt/OOH Forms/ calls
to actually render the form itself. Your application will then use 
that black box to obtain some input from the user. Your application
doesn't have to know how to handle user input, nor how to validate
input data, since internal methods will take care of that.

<p>This approach is very similar (I think) to <tt/OOH Forms/ one, only
at a higher level. <tt/OOH Forms/ elements have no way to communicate
with each other, and are only able to perform &quot;simple&quot; checks
on data integrity, while <tt/tpl_form/ adds a consistent interface for
complex data evaluation and processing.

<p>Furthermore, the <tt/get_default_values/ and <tt/set_default_values/
methods can be used to maintain user input between sessions, without
worrying about serialization of form variables (a BAD THING(tm)), using
an hash array containing field names and values.

<p>You'll note that an array is used to share data with the application.
You may object this is kinda futile, since all user input data may be
found in global vars and <tt/HTTP_POST/ or <tt/HTTP_GET/ global hashes.
This is true, and in the general case you'll pass back and forth an
empty array. The <tt/values/ variable is intended for use in very
complex data-entry setup, where a form behaviour may depend on previous
data entered by the user. In this case, if all forms cooperate reading
and writing to <tt/values/ hash array, final result may be constructed
step by step across multiple HTML pages. 
<!-- 
<p>The <tt/wizard/ class makes use of the <tt/values/ feature, thus 
allowing each of its forms to know what the user typed in previous
ones. 
-->

<sect2>Instance variables
<p>

<table>
<tabular ca="">
classname<colsep>
  Name of the class. Used for serialization AND in <tt/display/ to determine the filename of template containing HTML/php code needed to actually render the form.<rowsep> 
error<colsep>
  Contains error messages generated by <tt/validate/ and <tt/validate_input/ methods.<rowsep> 
values<colsep>
  This is a sort of &quot;shared memory area&quot; between the form and the application. Is read in <tt/init/ method and passed back in <tt/get_values/ method.<rowsep>
form_data<colsep>
  Holds form info (<tt/Form/ object).<rowsep>
has_defaults<colsep>
  Flag, form default values were passed via <tt/set_default_values/ method. Should not be tampered with by the user. 
</tabular>
<caption>Internal instance variables.</caption>
</table>

<sect2>Instance methods
<p>

<sect3>Accessible instance methods
<p>

<descrip>
<tag>init($values)</tag>
<p>
This is a sort of a constructor for the class. $values is an hash array
intended to store form values to be passed back to the application via
<tt/get_values/ method.

<tag>get_default_values()</tag>
<p>
Returns an array containing all data submitted by the user for the
form. This array is intended to be passed to <tt/set_defaults_values/
some time later.

<tag>set_default_values($fv)</tag>
<p>
Restore form defaults from an array as returned by <tt/get_default_values/. 

<tag>display()</tag>
<p>
Actually display form fields. This method should not be overridden in
descendants. User should instead provide a file named as the derived
class and with &quot;.ihtml&quot; extension which will be automatically
included.

<tag>get_values()</tag>
<p>
This method should not be overridden. It is intended as the main
interface between the application and the form. Once the form has
been properly derived to suit designer's needs, application calls
<tt/get_values/ and receives back the array passed to <tt/init/, 
eventually modified by <tt/process_input/ method, or <tt/false/
if user input is invalid. In that latter case, the application should
call <tt/display/ to (re)present the form to the user, eventually
filled with proper default values.

<tag>clear()</tag>
<p>
Sort of a &quot;destructor&quot;. There should no real need to call it,
except maybe freeing some memory. May be called from the application,
otherwise is not executed. Returns <tt/true/.

</descrip>

<sect3>Internal instance methods
<p>
<descrip>
<tag>setup()</tag>
<p>
Init the <tt/Form/ object, which will contain all fields info. The hidden
field <tt/form_name/, automatically added by this routine, is used by other
methods to determine if form has already been submitted by the
user. You shouldn't override this in descendants, use <tt/setup_fields/
instead. Returns <tt/true/.

<tag>setup_fields()</tag>
<p>
Override this method in order to provide form fields definition that
suit your needs.

<tag>validate()</tag>
<p>
Validates user input. This method should not be overridden in
descendants. See <tt/validate_input/ instead. Returns false on error
and sets <tt/error/ variable accordingly.

<tag>validate_input()</tag>
<p>
This method should be overridden in descendants, in order to provided
complex validation methods (i.e. field2 should not be empty IF
field1 == &quot;other&quot;).  Should return <tt/false/ on error and set
<tt/error/ variable with a sensible error message.

<tag>process()</tag>
<p>
Process user data. This method should not be overridden by descendants.
See <tt/process_input/ and <tt/process_default/ instead. Returns <tt/true/
on success, <tt/false/ otherwise.

<tag>process_input()</tag>
<p>
This method should be overridden in descendants. It is executed after
validation has taken place. The data passed to the form could be
used to fill <tt/values/ array.

<tag>process_default()</tag>
<p>
This method should be overridden in descendants. It is executed
when form validation fails or before presenting the form for the
first time. Should be used to bypass form displaying if data can
be extracted from previous actions, divination, penguin fly watching
or whatever.

</descrip>

<sect2>Example

<p>Suppose you have a form that the user should fill with her (eheh) name
and e-mail. You want to check wether this e-mail is valid, or your blind
date setup is lost. A... er... simple regular expression for validating
syntactically the e-mail is presented in the example code below.

<tscreen><code>
 $this-&gt;form_data-&gt;add_element(array(
  &quot;type&quot;=&gt;&quot;text&quot;,
  &quot;name&quot;=&gt;&quot;email&quot;,
  &quot;valid_e&quot;=&gt;&quot;Syntax error in E-Mail address.&quot;,
  &quot;valid_regex&quot;=&gt;&quot;^([-a-zA-Z0-9.]+@[-a-zA-Z0-9]+(\.[-a-zA-Z0-9]+)+)*$&quot;
 ));
</code></tscreen>

<p>Now, this piece of code should do the job, but since you're feeling
very paranoid today, you'd also like to validate the host name part of the
address with DNS. So, you put together some code which takes an hostname
in input and reports <tt/true/ on valid hostname, <tt/false/ otherwise
(HINT: on <htmlurl url="http://px.sklar.com/" name="PHP Code Exchange">
you should find a procedure for &quot;active&quot; email validation).

<p>Now that you have your shining new code, you can check the address.
The user fills out the form, you parse user input, no syntax errors,
time to call your <tt/mycheckhost/ from the application. If the function
is ok update your database, else load defaults into the form, display
again, close the page, goodbye.<p>

<p>I've done something similar for MANY forms, some of them with very
complex validation procedures, and I found that was too easy producing
very bad and unreadable code (well, I actually realized that the first
time I had to change some logic in data validation...).<p>

<p><tt/tpl_form/ should provide a solid framework to build your forms
with, and all the code will be self-contained and separated from main
application logic. Hope you'll like it.

<p>Time to see some code. First of all, class declaration, sub-classing
<tt/tpl_form/:

<tscreen><code>
class myform extends tpl_form {
  var $classname = &quot;myform&quot;;
  
  function setup_fields() {
    $this-&gt;form_data-&gt;add_element(array(
     &quot;name&quot;=&gt;&quot;email&quot;,
     ..., // See previous code snippet
    ));
    $this-&gt;form_data-&gt;add_element(array(
     &quot;name&quot;=&gt;&quot;submit&quot;,
     &quot;type&quot;=&gt;&quot;submit&quot;,
     &quot;value&quot;=&gt;&quot;submit&quot;
    ));
  }
  
  function validate_input() {
    global $email;
    list($uname, $hostname) = split(&quot;@&quot;, $email);
    if (! mycheckhost($hostname)) {
      $this-&gt;error = sprintf(&quot;Sorry, unknown host %s, try again.&quot;, $hostname);
      return false;
    }
    // Additional checks here...
    return true;
  }
}
</code></tscreen>

<p>You shuld provide a file <tt/myform.ihtml/ with HTML and php code to
render the form. Minimalistic example:

<tscreen><code>
&lt;html&gt;
&lt;body&gt;
&lt;?php
 $this-&gt;form_data-&gt;start_form($this-&gt;classname, &quot;POST&quot;, $sess-&gt;self_url(), &quot;&quot;);
 printf(&quot;%s&lt;br&gt;\n&quot;, $this-&gt;error);
 $this-&gt;form_data-&gt;show_element(&quot;email&quot;);
 printf(&quot;&lt;br&gt;\n&quot;);
 $this-&gt;form_data-&gt;show_element(&quot;submit&quot;);
 $this-&gt;form_data-&gt;finish();
?&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></tscreen>

<p>Your <tt/tpl_form/ class is complete, and will only need a little
work on the artistic side... 8-) To use your brand new class, include
class definition code into your application, then...

<tscreen><code>
$mf = new myform;
$mf-&gt;init(array()); // This is not strictly required, AT PRESENT,
                    // but I recommend it
if ($rv = $mf-&gt;getdata()) {
  $mf-&gt;clear(); // This is not strictly required, anyway it should free
                // some memory...
  global $email;
  // process your data at will
} else {
  $mf-&gt;display();
}
</code></tscreen>

<p>Hope this very little example does help in understanding the
real power of <tt/tpl_form/ class, at least in terms of 
rapid designing and code partitioning.
