[PHPLIB-DEV] Structure of the from stuff From: Ulf Wendel (ulf <email protected>)
Date: 04/20/00

Hi,

here's something I'm gonna place in CVS in pages/form/README when the
easter "holidays" end. It gives you some some background information to
understand the source.

Ulf

CVS/pages/form/README

All this is highly unstable.
API may change at any time.

Please referr to the examples for an API documentation.

+++ Files +++

php/form/
  form.inc - main class of a form
  form_elements.inc - as the names says...
  template_bridge.inc - bridge between form.inc and template.inc
  assistant.inc - kind of an assistant that guides you through
forms

/pages/form/
  example_*.inc - examples how use the base classes
        *.php3 - show off

+++ How to use it +++

All new classes form, template_bridge and assistant are used in a common
way,
they're programmed in an object oriented fashion. You always start
defining your
class that extends the base class. example_xy does not have a
constructor, but
it provides a method void Init(void) which describes/builds the form,
template_bridge or assistant. Depending on the type you will override
methods
(template_bridge, assistant) or even provide new ones (form, custom
validators).

class example_[form|tpl|assistant] extends [form|tpl|assistant] {
  function Init() {
           $this->addElement()
                 #$this->addComment
                 #$this->addPage()
                 [...]
        }
        [...]
}

This way you hide lots of code in include files and keep your script
free of dirty
form handling stuf. There're other side effects. Forms can be validated
without
writing tons of add_element() (oohforms) at the top of your script.
add_element()
was placed there to load the form with the submitted data.
autoloadValues()
can be placed in example_form->Init() to do this job for you. One line
"$f = new example_form(..)" replaces many others. template_bridge can
use a form
class without the need that you have to care on creating a form object
that uses
template_bridge...

+++ Hints on reading the code +++

When you're working on the source you'll sometimes want to "dump" your
object, have a look at the datastructures. Use the method
introspection()
which is in every class for debugging purpose to render a table that
shows all
methods and variables. (Don't use the slow Netscape to view this
table...).

Methods and instance variables are "documented" in way that looks
somewhat like
javadoc. The documentation always starts with "/**" and ends with "*/",
e.g:

/**
* A few words on what happens in this method
*  <email protected> [type] $variable explanation
*  <email protected> [type] $suggested_varname explanation
*  <email protected> [public|private]
*  <email protected> instancevar, anotherMethod()
*/

/**
* Comment on instance variables
*  <email protected> [type] $variable explanation
* explanation
*  <email protected> [public|private]
*  <email protected> instancevar, anotherMethod()
*/

The inline documentation takes about 22% of the amount of code. If
you're working with
PHP3 you can speed up the code about 5-7% by removing it.

Methodnames do not contain underscores, instance variables do.
All methods check their arguments, before they start working. Although
this
slows it down a little (~5%) it's very helpful when you're working with
high abstraction levels. The assistant objects contains of several
template_bridge objects each containing a form object with many
form_element
objects. Guess how long it takes to find out that a method in the
form_element
object is not working...

Every class has to two instance variables: (int)$errno and
(string)$error. Use
setError() and getError() to work on them. (boolean)$flag_showerror and
(boolean)$flag_haltonerror define how to react on errors:

function anotherMethod($options) {
  if (!is_array($options)) {
          $this->setError(99, "classname -> anotherMethod(), message");
                return false;
        }
        return true;
}

+++ form.inc, form_elements.inc +++

To use the form you must write a class example_form that extends form.
example_form
does not have a constructor, unless you know what you're doing.
example_form
must provide a method void Init(void), where the form_elements are
defined using
addElement(). The custom validators are defined in this class.

The basic structure of the old oohforms has remained. The form object
uses an array
to store form element objects: form->elements[ element_name ] =
(object)el . form
is responsible for managing the form elements:

- adding elements
- accessing the elements
- calling the element validators
- calling the custom validators
- generate the opening and closing <form> </form> Tag
- providing the javascript framework

All data of to the form elements is stored in the element objects -
without exceptions
(in contrast to the old oohforms, e.g. javascript index number).

All form element classes (text, submit, select...) in form_elements.inc
are derived
from a common class form_elements. form_elements provides prototypes for
all methods
that are public to form (none is public to the user - that's the job of
form...),
it provides you with a some methods
(writeJSCode(), doValidation()), that make it easy to write a new
element class.

Last but not least form_elements has some methods isXYType() that form
needs
to manage the elements. (No, I don't think it's an good idea to have
some element
data in the form object. This way it was done before and it's one reason
why it's
so hard to understand the oohforms).

form_elements cares on:

- storing the element data
- generating HTML, get(), getfrozen()
- freeze() and thawout()/unfreeze()
- retriving and setting the value, setValue(), getValue()
- validating the value, validate()
- creating the javascript, getJSCode()
- managing the validation flags of certain events

Your form_element_[text...] class is derived from form_elements. It
holds all
instance variables that are not common for all types and it overrides
the methods are exclusive to your type, at least:

get() - generate HTML
getfrozen() - generate HTML in a frozen style
validate() - validate the element (default validator)
generateJS() - create some JavaScript validation code

Have a closer look at validate() and generateJS().
validate() uses the method doValidation() provided by form_elements to
find out whether to react on a certain event (length, valid, intro).
generateJS() does not write a complete function or a if-else-endif
construct, it uses writeJSCode($condition, $errmsg) to embed a condition
in a bigger peace of Code.

+++ template_bridge.inc +++

template_bridge is used to "render" a form using a template. It manages
several
form elements of an form object/class (yes you can pass a classname or
an object to
template_bridge) and some additional information.

We've played a lot with forms and noticed, that you it's a good idea to
have
this choices to give a user response:

- placing a label in front of the form elemen (addElement()->label)
- placing instructions near the form element (addElement()->hint)
- placing a colored error message beside
  the form element (form->length_e, valid_e, intro_e)
- placing an image indicating the current status of an element
  in front of of it (error, ok, optional)
- using a background color to indicate the current status
(- using comments to seperate blocks of elements, addComment() )

Due to the limitations a template makes, I decided to double
form->[get]Start,
form->[get]Finish in the API.
getSessionHash() and restoreSessionHash() are used by assistant (see
below).

The source is not really hard, give it a try...

+++ assistant.inc +++

Ok, wake up again, what happened yet is not really new nor exiting.
assistant
takes several classes that implement certain methods and render forms to
guide the
user trough the forms/"pages". Depending on the mode the user is allowed
to switch
between the pages always or he has must finish the current page before
he's allowed
to switch to another page. The assistant is drawn using a template.

assistant cares on:
 - managing (loading, restoring, retriving) the pages
 - drawing tabs to switch pages
 - controling the tabs
 - drawing buttons: "prev", "next", "reset", "save the current page"
 - drawing buttons: "exit" and "help"
 - creating the opening and closing <form></form> tags
 
Assistant can use any class as a "page". The class must provide a
certain interface,
such as template_bridge does:

- string get(void), get the form (html)
- boolean restoreSessionHash(array $values), kind of persistant_slots
e.g. restores form values
- array getSessionHash(void), kind of persistant_slots e.g. returns
form values
- boolean setMethod(string),
form->autoloadValues()/form_elements->getValue() needs to know
- boolean isValid(void), is the form filled out correct?

You may wonder about restoreSessionHash(), getSessionHash(). Users
switching between
pages expect that pages do not forget their values. assistant has to
make sure
that a page is reloaded once accepted values when it comes back in
front.
assistant must be placed in the session to be able to handle this job.
Now you could
eigther store all assistant pages (each containing dozens of objects) in
the session
or save the form values. The second makes the session data as small as
possible.
assistant puts the form values, the page validation flags and the name
of the last
viewed page in the session.

Restoring assistant is done by the session, but assistant has to compute
some values
before it can be used. restoreAssistant() saves the values of the last
viewed page, sets
the page errors so that the tabs can be drawn and finds out the
requested/allowed pages.
restoreAssistant() must be called at the end of Init(), which you have
to call manually
in your script.

When the user has finished all pages he's allowed to press a finish
button. A pressed
finish button does not guarantee that last pages is valid. The user
might have
deletet some values before he pressed the finish button. Anyway as soon
as $assistant->isValid() returns true, you should use getPageValues() to
save
the collected form values.

Voila, have fun with assistant.
-
PHPLIB Developers Mailing List. Send messages to <phplib-dev <email protected>>.
To unsubscribe, send "unsubscribe" to <phplib-dev-request <email protected>> in
the body, not the subject, of your message.