Actions

Developer Area/Core Subsystems/Form API (Pieforms): Difference between revisions

From Mahara Wiki

< Developer Area‎ | Core Subsystems
m (I found typo in pieform code.)
Line 16: Line 16:
Here is a simple form definition:
Here is a simple form definition:


<div class="plugin tightenable"><code><font color="#000000"> <font color="#0000bb">$form </font><font color="#007700">= array(<br />     </font><font color="#dd0000">'name' </font><font color="#007700">=&gt; </font><font color="#dd0000">'testform'</font><font color="#007700">,<br />     </font><font color="#dd0000">'method' </font><font color="#007700">=&gt; </font><font color="#dd0000">'post'</font><font color="#007700">,<br />     </font><font color="#dd0000">'action' </font><font color="#007700">=&gt; </font><font color="#dd0000"><nowiki>''</nowiki></font><font color="#007700">,<br />     </font><font color="#dd0000">'elements' </font><font color="#007700">= array(<br />         </font><font color="#dd0000">'login' </font><font color="#007700">=&gt; array(<br />             </font><font color="#dd0000">'type'   </font><font color="#007700">=&gt; </font><font color="#dd0000">'fieldset'</font><font color="#007700">,<br />             </font><font color="#dd0000">'legend' </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'login'</font><font color="#007700">),<br />             </font><font color="#dd0000">'elements' </font><font color="#007700">=&gt; array(<br />                 </font><font color="#dd0000">'login_username' </font><font color="#007700">=&gt; array(<br />                     </font><font color="#dd0000">'type'        </font><font color="#007700">=&gt; </font><font color="#dd0000">'text'</font><font color="#007700">,<br />                     </font><font color="#dd0000">'title'       </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'username'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'description' </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'usernamedesc'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'help'        </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'usernamehelp'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'rules' </font><font color="#007700">=&gt; array(<br />                         </font><font color="#dd0000">'required'    </font><font color="#007700">=&gt; </font><font color="#0000bb">true<br />                     </font><font color="#007700">)<br />                 ),<br />                 </font><font color="#dd0000">'login_password' </font><font color="#007700">=&gt; array(<br />                     </font><font color="#dd0000">'type'        </font><font color="#007700">=&gt; </font><font color="#dd0000">'password'</font><font color="#007700">,<br />                     </font><font color="#dd0000">'title'       </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'password'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'description' </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'passworddesc'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'help'        </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'passwordhelp'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'value'       </font><font color="#007700">=&gt; </font><font color="#dd0000"><nowiki>''</nowiki></font><font color="#007700">,<br />                     </font><font color="#dd0000">'rules' </font><font color="#007700">=&gt; array(<br />                         </font><font color="#dd0000">'required'    </font><font color="#007700">=&gt; </font><font color="#0000bb">true<br />                     </font><font color="#007700">)<br />                 )<br />             )<br />         ),<br /><br />         </font><font color="#dd0000">'submit' </font><font color="#007700">=&gt; array(<br />             </font><font color="#dd0000">'type'  </font><font color="#007700">=&gt; </font><font color="#dd0000">'submit'</font><font color="#007700">,<br />             </font><font color="#dd0000">'value' </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'login'</font><font color="#007700">)<br />         )<br />     )<br /> );<br /><br /> function </font><font color="#0000bb">testform_validate</font><font color="#007700">(</font><font color="#0000bb">Pieform $form</font><font color="#007700">, </font><font color="#0000bb">$values</font><font color="#007700">) {<br />     if (!</font><font color="#0000bb">$form</font><font color="#007700">-&gt;</font><font color="#0000bb">get_error</font><font color="#007700">(</font><font color="#dd0000">'login_username'</font><font color="#007700">) &amp;&amp; !</font><font color="#0000bb">validate_username</font><font color="#007700">(</font><font color="#0000bb">$values</font><font color="#007700">[</font><font color="#dd0000">'login_username'</font><font color="#007700">])) {<br />         </font><font color="#0000bb">$form</font><font color="#007700">-&gt;</font><font color="#0000bb">set_error</font><font color="#007700">(</font><font color="#dd0000">'login_username'</font><font color="#007700">, </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'usernamenotvalid'</font><font color="#007700">));<br />     }<br /> }<br /><br /> function </font><font color="#0000bb">testform_submit</font><font color="#007700">(</font><font color="#0000bb">$values</font><font color="#007700">) {<br />     global </font><font color="#0000bb">$SESSION</font><font color="#007700">;<br />     </font><font color="#0000bb">try </font><font color="#007700">{<br />         </font><font color="#0000bb">log_user_in</font><font color="#007700">(</font><font color="#0000bb">$values</font><font color="#007700">);<br />         </font><font color="#0000bb">$SESSION</font><font color="#007700">-&gt;</font><font color="#0000bb">add_ok_msg</font><font color="#007700">(</font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'userloggedinok'</font><font color="#007700">));<br />     }<br />     </font><font color="#0000bb">catch </font><font color="#007700">(</font><font color="#0000bb">SQLException $e</font><font color="#007700">) {<br />         </font><font color="#0000bb">$SESSION</font><font color="#007700">-&gt;</font><font color="#0000bb">add_err_msg</font><font color="#007700">(</font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'couldnotloguserin'</font><font color="#007700">));<br />     }<br />     </font><font color="#0000bb">redirect</font><font color="#007700">(</font><font color="#0000bb">get_config</font><font color="#007700">(</font><font color="#dd0000">'wwwroot'</font><font color="#007700">));<br /> }<br /><br /></font><font color="#0000bb">$smarty </font><font color="#007700">= </font><font color="#0000bb">smarty</font><font color="#007700">();<br /></font><font color="#0000bb">$smarty</font><font color="#007700">-&gt;</font><font color="#0000bb">assign</font><font color="#007700">(</font><font color="#dd0000">'form'</font><font color="#007700">, </font><font color="#0000bb">pieform</font><font color="#007700">(</font><font color="#0000bb">$form</font><font color="#007700">));<br /></font><font color="#0000bb">$smarty</font><font color="#007700">-&gt;</font><font color="#0000bb">display</font><font color="#007700">(</font><font color="#dd0000">'template.tpl'</font><font color="#007700">);<br /></font> </font> </code></div>
<div class="plugin tightenable"><code><font color="#000000"> <font color="#0000bb">$form </font><font color="#007700">= array(<br />     </font><font color="#dd0000">'name' </font><font color="#007700">=&gt; </font><font color="#dd0000">'testform'</font><font color="#007700">,<br />     </font><font color="#dd0000">'method' </font><font color="#007700">=&gt; </font><font color="#dd0000">'post'</font><font color="#007700">,<br />     </font><font color="#dd0000">'action' </font><font color="#007700">=&gt; </font><font color="#dd0000"><nowiki>''</nowiki></font><font color="#007700">,<br />     </font><font color="#dd0000">'elements' </font><font color="#007700">=> array(<br />         </font><font color="#dd0000">'login' </font><font color="#007700">=&gt; array(<br />             </font><font color="#dd0000">'type'   </font><font color="#007700">=&gt; </font><font color="#dd0000">'fieldset'</font><font color="#007700">,<br />             </font><font color="#dd0000">'legend' </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'login'</font><font color="#007700">),<br />             </font><font color="#dd0000">'elements' </font><font color="#007700">=&gt; array(<br />                 </font><font color="#dd0000">'login_username' </font><font color="#007700">=&gt; array(<br />                     </font><font color="#dd0000">'type'        </font><font color="#007700">=&gt; </font><font color="#dd0000">'text'</font><font color="#007700">,<br />                     </font><font color="#dd0000">'title'       </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'username'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'description' </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'usernamedesc'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'help'        </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'usernamehelp'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'rules' </font><font color="#007700">=&gt; array(<br />                         </font><font color="#dd0000">'required'    </font><font color="#007700">=&gt; </font><font color="#0000bb">true<br />                     </font><font color="#007700">)<br />                 ),<br />                 </font><font color="#dd0000">'login_password' </font><font color="#007700">=&gt; array(<br />                     </font><font color="#dd0000">'type'        </font><font color="#007700">=&gt; </font><font color="#dd0000">'password'</font><font color="#007700">,<br />                     </font><font color="#dd0000">'title'       </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'password'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'description' </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'passworddesc'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'help'        </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'passwordhelp'</font><font color="#007700">),<br />                     </font><font color="#dd0000">'value'       </font><font color="#007700">=&gt; </font><font color="#dd0000"><nowiki>''</nowiki></font><font color="#007700">,<br />                     </font><font color="#dd0000">'rules' </font><font color="#007700">=&gt; array(<br />                         </font><font color="#dd0000">'required'    </font><font color="#007700">=&gt; </font><font color="#0000bb">true<br />                     </font><font color="#007700">)<br />                 )<br />             )<br />         ),<br /><br />         </font><font color="#dd0000">'submit' </font><font color="#007700">=&gt; array(<br />             </font><font color="#dd0000">'type'  </font><font color="#007700">=&gt; </font><font color="#dd0000">'submit'</font><font color="#007700">,<br />             </font><font color="#dd0000">'value' </font><font color="#007700">=&gt; </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'login'</font><font color="#007700">)<br />         )<br />     )<br /> );<br /><br /> function </font><font color="#0000bb">testform_validate</font><font color="#007700">(</font><font color="#0000bb">Pieform $form</font><font color="#007700">, </font><font color="#0000bb">$values</font><font color="#007700">) {<br />     if (!</font><font color="#0000bb">$form</font><font color="#007700">-&gt;</font><font color="#0000bb">get_error</font><font color="#007700">(</font><font color="#dd0000">'login_username'</font><font color="#007700">) &amp;&amp; !</font><font color="#0000bb">validate_username</font><font color="#007700">(</font><font color="#0000bb">$values</font><font color="#007700">[</font><font color="#dd0000">'login_username'</font><font color="#007700">])) {<br />         </font><font color="#0000bb">$form</font><font color="#007700">-&gt;</font><font color="#0000bb">set_error</font><font color="#007700">(</font><font color="#dd0000">'login_username'</font><font color="#007700">, </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'usernamenotvalid'</font><font color="#007700">));<br />     }<br /> }<br /><br /> function </font><font color="#0000bb">testform_submit</font><font color="#007700">(</font><font color="#0000bb">$values</font><font color="#007700">) {<br />     global </font><font color="#0000bb">$SESSION</font><font color="#007700">;<br />     </font><font color="#0000bb">try </font><font color="#007700">{<br />         </font><font color="#0000bb">log_user_in</font><font color="#007700">(</font><font color="#0000bb">$values</font><font color="#007700">);<br />         </font><font color="#0000bb">$SESSION</font><font color="#007700">-&gt;</font><font color="#0000bb">add_ok_msg</font><font color="#007700">(</font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'userloggedinok'</font><font color="#007700">));<br />     }<br />     </font><font color="#0000bb">catch </font><font color="#007700">(</font><font color="#0000bb">SQLException $e</font><font color="#007700">) {<br />         </font><font color="#0000bb">$SESSION</font><font color="#007700">-&gt;</font><font color="#0000bb">add_err_msg</font><font color="#007700">(</font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'couldnotloguserin'</font><font color="#007700">));<br />     }<br />     </font><font color="#0000bb">redirect</font><font color="#007700">(</font><font color="#0000bb">get_config</font><font color="#007700">(</font><font color="#dd0000">'wwwroot'</font><font color="#007700">));<br /> }<br /><br /></font><font color="#0000bb">$smarty </font><font color="#007700">= </font><font color="#0000bb">smarty</font><font color="#007700">();<br /></font><font color="#0000bb">$smarty</font><font color="#007700">-&gt;</font><font color="#0000bb">assign</font><font color="#007700">(</font><font color="#dd0000">'form'</font><font color="#007700">, </font><font color="#0000bb">pieform</font><font color="#007700">(</font><font color="#0000bb">$form</font><font color="#007700">));<br /></font><font color="#0000bb">$smarty</font><font color="#007700">-&gt;</font><font color="#0000bb">display</font><font color="#007700">(</font><font color="#dd0000">'template.tpl'</font><font color="#007700">);<br /></font> </font> </code></div>


Here is a description of each part.
Here is a description of each part.

Revision as of 15:17, 24 Mayıs 2017

Pieforms is the forms library used by Mahara. It provides a unified way to create, validate and process forms all with a common look and feel, with support for pluggable elements, renderers and validation rules.


Concepts

To create a form, you need three or four things

  • A hash containing the form definition. Basically, this is a big array describing overall form properties, and the elements for the form.
  • An optional callback function that can be used to validate a form submission.
  • A callback function that handles the submission of the form, given that all of the values submitted have been validated
  • A call to the form() function, passing the form definition, which manages the entire form building, validating, submitting and displaying process.

A simple form

Here is a simple form definition:

$form = array(
    
'name' => 'testform',
    
'method' => 'post',
    
'action' => '',
    
'elements' => array(
        
'login' => array(
            
'type'   => 'fieldset',
            
'legend' => get_string('login'),
            
'elements' => array(
                
'login_username' => array(
                    
'type'        => 'text',
                    
'title'       => get_string('username'),
                    
'description' => get_string('usernamedesc'),
                    
'help'        => get_string('usernamehelp'),
                    
'rules' => array(
                        
'required'    => true
                    
)
                ),
                
'login_password' => array(
                    
'type'        => 'password',
                    
'title'       => get_string('password'),
                    
'description' => get_string('passworddesc'),
                    
'help'        => get_string('passwordhelp'),
                    
'value'       => '',
                    
'rules' => array(
                        
'required'    => true
                    
)
                )
            )
        ),

        
'submit' => array(
            
'type'  => 'submit',
            
'value' => get_string('login')
        )
    )
);

function
testform_validate(Pieform $form, $values) {
    if (!
$form->get_error('login_username') && !validate_username($values['login_username'])) {
        
$form->set_error('login_username', get_string('usernamenotvalid'));
    }
}

function
testform_submit($values) {
    global
$SESSION;
    
try {
        
log_user_in($values);
        
$SESSION->add_ok_msg(get_string('userloggedinok'));
    }
    
catch (SQLException $e) {
        
$SESSION->add_err_msg(get_string('couldnotloguserin'));
    }
    
redirect(get_config('wwwroot'));
}

$smarty = smarty();
$smarty->assign('form', pieform($form));
$smarty->display('template.tpl');

Here is a description of each part.

The $form array

This array contains the definition of the form. In this example the form is hard coded, but there's nothing to stop it from being generated dynamically. The array specifies some metadata for the whole form, like its name, method and action target, and then a list of elements that are on the form. Every form must have a name, this name is used to call the callback functions.

More information about the $form array is available on a separate page.

The testform_validate function

This function is named after the form name, with _validate on the end. It takes an array of the submitted values, and the form that is being validated, and can perform whatever checks are required that cannot be otherwise handled by the inbuilt form rules. If an error is detected, the method $form->set_error is called, to set an error message on the field.

There are some inbuilt rules available for every element, such as "required", "minlength" and "maxlength". Both elements in the example form are required. These checks are performed before the validate function is called and will automatically set errors, which is why the check to get_error is made.

the testform_submit function

This function is named after the form name, with _submit on the end. It takes an array of the submitted values that have already been validated successfully according to the validation rules set on the form. From here you can do almost anything, but the last thing that you should do is somehow display another page, otherwise the form will be re-presented to the user.

Most of the time you will display another page by using the File:Http.pngredirect function, which takes you to another page. To set a success/fail message, you can use the $SESSION object to add messages, and they will be displayed on the next page that the user visits.

The pieform($form) function call

This call builds the form, validates and submits the form if it has been submitted, and returns the HTML for the form. So in the example above the form is simply assigned to a smarty template variable, which will be in the template wherever you want the form to be.

Form API Terms

Pieforms has several components, a basic description of each is given here.

Elements

Pieform elements are similar to the widgets you have in normal HTML forms, and in fact all HTML widgets have element implementations in Pieforms. However, Pieforms does not limit you to using just the standard HTML widgets - you can write your own elements that have very advanced behaviour, are comprised of multiple widgets, or do almost anything you like.

For example, there is a 'text' element, which represents a simple HTML widget. But there is also a 'date' widget, which is currently rendered as three widgets for year, month, day, a 'wysiwyg' element which represents a full WYSIWYG editor, and a 'userlist' element that is made up of two boxes, with controls to move usernames from one to the other, and even allows ajax searching of the lists!

It's really easy to write your own elements, and they can be reused on any form that needs them. More information about elements, including how to write your own, is available.

Rules

Rules are used to validate elements. Given the element's value, they check if the value matches whatever condition they check. As of 2006/11/11 there are only four rules - required, minlength, maxlength and email - but again like elements, they are pluggable, so many more can be written.

Rules may apply differently to different elements, so elements can define a function that specifies how a rule applies to them, which allows some flexibility in rule reuse. (todo: actually, this is a lie - it only applies to the 'required' rule at the moment, and needs to be implemented at some point).

More information on rules? is available.

Renderers

Renders are used to change the containers that elements are rendered inside. For example, there is a 'table' renderer which puts each element inside a table row, and a 'div' renderer that puts them inside a

. Again, these are pluggable to suit your needs.

One thing that the Mahara project has done has added support for contextual help for each element. The renderer will render question marks beside each form element, which when clicked, reveal contextual help for the element. You could add almost any extra information to each element, to assist your form output - for example, a description, help text or raw HTML to be output at a certain point for each element.

Renderers? have their own page for more information (can you tell I'm running out of ways to say the same thing? :)

How they fit together

Here are some statements that might help you describe how a form is put together using the form API.

A form is made up of elements.

Elements are like standard HTML widgets, except they can be more complicated and comprised of many widgets if necessary.

Elements are validated by rules?.

Each element in a form is rendered inside some boilerplate HTML by a renderer?.

When forms are submitted, they are validated using the rules for each element. If validation passes, the form submit callback is called with the values. If validation fails, the form is redisplayed, with the renderer drawing the error messages as returned by the rule for each element next to it.

Other features of the Form API

As well as customisable elements, renderers and rules, the form API has the following features
:* send the form by POST or GET
  • send the form to different pages for processing (as long as the form definition is available in both the sending and receiving scripts)
  • support for cancel buttons - in effect, submit buttons with no validation applied to them
  • auto focus of fields in a form on page load, including focussing the first element with an error if there is one
  • ajax submission of the form, including display of success/failure notices
  • automatic handling of tabindex for each element
  • support for just rendering the form and not handling submission if required
  • HTML 4.01 compliance

Major TODO items

  • Finish off javascript error setting, preferably so that the error HTML is exported by renderers as Mochikit DOM code, and that the 'error' class is set on the elements.
  • Add some more element types, like autocomplete. Implement 'wysiwyg' properly. Change the date picker to user a calendar.
  • Handle multiple submit buttons
  • Handle multipage forms
  • Handle a tabbed interface type form

Subpages

Relevant links