InterJinn

Development Framework For PHP

FormJinn

One of the most often used feature of any interactive web application is the form. Forms allow a visitor to input information and have it sent to the server for processing. There are many different ways to go about this process. You could directly compose the forms yourself via HTML which would provide simplistic functionality and subsequently you would need to carefully validate the information so that your database (where applicable) doesn't become corrupt. Alternatively you could use the FormJinn engine which provides a separation of data, layout, and display (albeit not yet integrated into TemplateJinn). This page will server to walk through the steps necessary to create a form using the FormJinn service.

A breakdown of FormJinn

The first thing to know is how forms are constructed. there are 5 basic parts to any FormJinn form.

  1. FormJinn service for retrieving form objects
  2. Form objects which will contain fields and form information
  3. Field objects which contain individual field data
  4. WidgetJinn service for rendering fields
  5. InputProcessor for validating and processing field input
Although there are 5 parts, you will most likely only ever deal with 4 of them. The InputProcessor engine is implicitly called on the field's data at submission time. You will just declare types of processors to apply to the data.

Creating a form

We will work through an example form which will request the user's name, phone number, and email address. The first step is to create a form. Creating a form is quite simple. You just request the Formjinn service and then you use it to create a form object (from within a component or service).

Example:
$formJinn = $this->getService( 'formJinn' );
$userInfoForm = $formJinn->do->createForm( 'userInfo' );
You now have a form object -- it's that simple. Next you will want to set the destination page to load when the form has been successfully submitted. This requires that you set the form's actionURL field.
Example:
$userInfoForm->do->setData( 'actionURL', '//thankYou.php' );
There you go! You've created your first FormJinn form. It doesn't do anything yet, but it will as soon as you start adding and rendering fields for it.

Creating a form field

Now that we've created a form which will keep track of the user's info fields, it is now time to add our first field -- the name field. Creating a field is almost as simple as creating the form itself. there are actually a couple of ways to go about creating a field and then subsequently the widget -- for now I will just describe the technique I have found simplest from experience.

Example:
$nameField = $userInfoForm->do->createField( 'name', 'input' );
There! We've created a name field which will hold the user's name. Now we'll just go ahead and create the phone, email, and submit button fields.
$phoneField = $userInfoForm->do->createField( 'phone', 'input' );
$emailField = $userInfoForm->do->createField( 'email', 'input' );
$submitField = $userInfoForm->do->createField( 'submit', 'submit' );
That was all simple enough, but what about giving the phone field a default value? We can do that by setting it's value.
Example:
$phoneField->do->setValue( '(xxx) xxx-xxxx' );
Our default value for the phone number just represents the format in which we would like the user to input their phone number. You'll see later that for North American phone numbers this is not really an issue.

Displaying the form and its fields

Now that you've created a form, and some simple fields, the next question you should be asking is "How do I display these to the user?" the answer to this is fairly simple -- you just create a field widget object which will be used to render the field. We obtain a widget in the following example:

Example:
$widgetJinn = $this->getService( 'widgetJinn' );
$nameWidget = $widgetJinn->do->getWidget( $nameField );
As I said earlier, there are two ways to go about creating a field. The following example illustrates another two possible methods. I personally find the first example to generate clearer code.
Example:
//
// Alternative 1.
//
$nameField = $userInfoForm->do->createField( 'name' );
$nameField->do->setType( 'input' );
$widgetJinn = $this->getService( 'widgetJinn' ); $nameWidget = $widgetJinn->do->getWidget( $nameField );
// // Alternative 2. // $nameField = $userInfoForm->do->createField( 'name' ); $nameField->do->setType( 'input' );
$widgetJinn = $this->getService( 'widgetJinn' ); $nameWidget = $widgetJinn->do->getWidget( 'input' ); $nameWidget->do->setField( $nameObject );
Whichever style you use is up to you. Having created a widget for the user's name field, we now need to create one for the phone and email fields.
Example:
$phoneWidget = $widgetJinn->do->getWidget( $phoneField );
$emailWidget = $widgetJinn->do->getWidget( $emailField );
$submitWidget = $widgetJinn->do->getWidget( $submitField );
We're almost ready to display our form, but there's some widget configuration that still needs to take place. We should set up the width of the input fields and the fonts to use for the widget's label and errors. I'll do it for the name widget and submit widget and leave the others as a trivial exercise:
Example:
$normalFont = $this->getService( 'htmlFont' );
$normalFont->do->setAttribute( 'face', 'verdana, arial' );
$normalFont->do->setAttribute( 'size', '-1' );
$normalFont->do->setAttribute( 'color', '#000000' );
$errorFont = $this->getService( 'htmlFont' ); $errorFont->do->setAttribute( 'face', 'verdana, arial' ); $errorFont->do->setAttribute( 'size', '-1' ); $errorFont->do->setAttribute( 'color', '#aa0000' );
$smallErrorFont = $this->getService( 'htmlFont' ); $smallErrorFont->do->setAttribute( 'face', 'verdana, arial' ); $smallErrorFont->do->setAttribute( 'size', '-2' ); $smallErrorFont->do->setAttribute( 'color', '#aa0000' );
$nameWidget->do->setAttribute( 'size', 20 ); $nameWidget->do->setFont( $normalFont ); $nameWidget->do->setErrorFont( $normalFont );
$submitWidget->do->setAttribute( 'value', 'Submit Form' ); $submitWidget->do->setFont( $normalFont );
It is worth mentioning that fonts can be created as properties for components, which makes them immediately available without the need to set the individual attributes. By declaring font properties at the root of your site's property hierarchy, you will be able to share the same definition throughout your components. Also note that a font could have been created and had only its class attribute set which would make preferable use of cascading stylesheets. Now is time for us to go ahead and display our form.
Example:
$userInfoForm->do->open();
$nameWidget->do->paintError( $smallErrorFont, '', '<br />' ); $nameWidget->do->paintText( 'Please input your name: <br />' ); $nameWidget->do->paint();
echo '<br /><br />';
$phoneWidget->do->paintError( $smallErrorFont, '', '<br />' ); $phoneWidget->do->paintText( 'Input your phone number: <br />' ); $phoneWidget->do->paint();
echo '<br /><br />';
$emailWidget->do->paintError( $smallErrorFont, '', '<br />' ); $emailWidget->do->paintText( 'Input your email address: <br />' ); $emailWidget->do->paint();
echo '<br /><br />';
$userInfoForm->do->close();
If you have done everything correctly up until now you should have a working form. The form produced in the above example is shown at the bottom of this page if you would like to compare (it is not exactly the same but incorporates additional features explained as we continue). This may seem like a lot of work so far, and admittedly it's more than just using HTML directly. The advantages should become very apparent shortly.

Validating user input

One of the claims of FormJinn is that it simplifies the task of validating user input. This is done by applying input processors to the field's data. When the field is submitted, its data will be passed to any registered processor. If any of these processors return false then the field will be considered invalid, and subsequently the form will also be considered invalid. To begin we will apply some processors to the name field.

Example:
$nameProcesses = array
(
    array
    (
        'type'         => 'mandatory',
        'errorMessage' => 'You must input your name!',
    ),
    array
    (
        'type'         => 'regEx',
        'expression'   => '^[[:alpha:] ]*$',
        'errorMessage' => 'You may only use letters and spaces!',
    ),
);
$nameField->do->setData( 'validation', $nameProcesses );
The above can be placed anywhere after the name field has been created. It won't actually have any affect until the form has been submitted. It should be fairly obvious from observation how this is intended to affect the input. The processes array consists of as many arrays as you have processes to operate on the input. Each array entry in the processes array denotes a particular type of processor, and the order of the processes denots the order in which the data will be subjected to evaluation. Our processes in the above example first check that the user has input some data, then if that passes, the second processor checks that the name only consists of letters and spaces. If any of these fail then in our code that displays the widgets, the error message will be displayed using the $smallErrorFont and the input label will be displayed using the $errorFont instead of the $normalFont. Try inputting data into the name field at the bottom of this page to see how this works. Similarly we add validation for the phone number and for the email fields.
Example:
$phoneProcesses = array
(
    array
    (
        'type'         => 'mandatory',
        'errorMessage' => 'You must input your phone number!',
    ),
    array
    (
        'type'         => 'phone',
        'errorMessage' => 'The input phone number is invalid!',
    ),
);
$phoneField->do->setData( 'validation', $phoneProcesses );
$emailProcesses = array ( array ( 'type' => 'mandatory', 'errorMessage' => 'You must input your email address!', ), array ( 'type' => 'email', 'errorMessage' => 'The input email address is invalid!', ), );
$emailField->do->setData( 'validation', $emailProcesses );

What else can I do?

By now you should have a fairly good idea of how the FormJinn system works. There's a lot more that hasn't been covered here, such as custom processors, preProcessing, which occurs before validation, and postProcessing, which occurs after validation. Also you can replace the regular submit button with an image submission, there are widgets for drop down select lists, uploading files, and everything else you usually do in forms (unless I've forgotten something :). You'll also notice that if you have cookies enabled, then when you got to other pages and return to the form your values will not have changed. This might pose a problem if you always want to reset the value of a field to some default, and so the following mechanism is possible:

Example:
$phoneField->do->setValue( '(xxx) xxx-xxxx', true );
The second parameter lets the field know to force the new default value to take effect. Of course if the form has just been submitted forcing the value still won't work. However if you leave the page and return then the default will be back again.

The userInfo form

Below is the form you should have been able to produce if you followed and understood the material in this page. If not then you can see the complete listing for the form here.