Models - input form

This tutorial shows how to use class Form to render data input forms from models and then put posted data into models to add or update them.

Creating form object

$form = new Form();
/* name the form */
$form = new Form('formname');
/* to return from object to create state */

$form->reset();
$form->reset('newFormName');

When creating a form object a name can be specified. Naming a form will cause all widgets names inside the form to have form name as prefix. For example for a form without a name form widget names are such as: modelname-propertyname or myWidgetName but for a form with name they would become formname-modelname-propertyname and formname-myWidgetName. Naming a form can be useful when more than one forms are used in a page from the same model, to prevent form widgets from having the same tag id.

Method reset clears all configs and returns form object to create state. When rendering more than one form with a form object reset the object before config up new form.

Note: when some complex widgets are used in a form, a form objects may add CSS and/or JavaScript files or JavaScript codes to factory context. For the form to work correctly this sources need to be loaded/executed after printing form HTML on the screen.

Adding inputs

Method add adds inputs and widgets to a form. This method accepts a variety of different set of arguments and can be used in many different ways as described below:

Adding model

$user = new User();

/* add all model properties to form */
$form->add($user);

/* add selective model properties to form */
$form->add($model, 'name', 'Group', 'City', 'active');
$form->add($model, array('name', 'Group', 'City', 'active'));

/* add selective model properties
   elegant way - property list for add form is defined inside model */
$form->add($model, User::colsAdd());

Widgets are chosen based on property definitions. Using widget key in property definition sets the widget strictly. Otherwise data type and other definition factors are used to pick the best available widget.

Widget label comes from:

  • Model property definition. For example: 'label' => 'First name',
  • Model method label. If property exists in return array {property-name: label,}
  • If property is model object or model object list then label will be inherited from it's model presenter property label.
  • Model property name

Class Form translates labels using gettext (if application is multi lingual);

If model object is filled with data for example after a query, the form inputs and widgets will be contain property data, so the form would be an update form.

Adding HTML element

Method add lets adding an HTML element to the from if method parameters are 1-label 2-element type 3-attributes 4-parameters. The list below shows the constant to be used as element type:

  • Form::INPUT
  • Form::TEXT
  • Form::FILE
  • Form::CHECKBOX
  • Form::RADIO
  • Form::IMG
  • Form::BUTTON
  • Form::IBUTTON
  • Form::SUBMIT
  • Form::RESET
  • Form::IMAGE
  • Form::LABEL
  • Form::PASSWORD
  • Form::HIDDEN
  • Form::DIV
  • Form::ANCHOR

This example shows how:

$form->add(null, Form::INPUT);

$form->add('My label', Form::INPUT);

$form->add(
    _('My input'),
    Form::INPUT,
    // attributes - optional argument
    array(
        'name'      => 'my-input', // id and name attributes
        'class'     => 'required int',
        'maxlength' => '12',
        'title'     => _('This is my input'),
    ),
    // params - optional argument
    array(
        'unit' => 'Km',
    )
);

$form->add(
    null,
    Form::BUTTON,
    array(
        'text'    => _('Press me'),
        'onclick' => 'alert("Hello!");',
    )
);

$form->add(
    null,
    Form::DIV,
    array(
        'name' => 'my-div',
        'text' => _('This is a message'),
    )
);

$form->add(
    null,
    Form::ANCHOR,
    array(
        'text' => 'artaengine.com',
        'href' => 'http://www.artaengine.com',
    )
);

Adding a Form::PASSWORD may add an additional confirm password row:

// this will only add one row for password input
$form->confirmPassword = null;
$form->add('Password', Form::PASSWORD);

/* this will add two rows one for password input
   and one for confirm */
$form->confirmPassword = _('Confirm password'); // label for second input
$form->add('My label', Form::PASSWORD);

Shortcuts

Instead of using method add shortcuts can be used to add HTML elements:

$form->button(_('Click me'), 'biteMe();');
$form->button(_('Click me'), 'onClickDo();', 'button-id-name');

$form->submit(_('Click me'), 'attr-name');
$form->submit(_('Click me'), 'attr-name', 'attr-class', 'onClickDo();');

$form->hidden('attr-name', 'attr-value');

$form->anchor('Artaengine', 'http://www.artaengine.com');
$form->anchor('Bite me!', '', 'biteMe();');
$form->anchor('Bite me!', '#', 'biteMe();', 'class-attr-value');

$form->div('My label', 'my-div-id', 'Hello there! im a div');
$form->div(null, 'my-div-id', 'Hello there! im a div', 'class-attr-value');

$form->label('My label', 'Hello there! im a label');
$form->label('', 'Hello there! im a label', 'label-id', 'class-attr-value');

$form->img('http://a.com/img/3.png');
$form->img('http://a.com/img/3.png', 'alt-attr-value');
$form->img('http://a.com/img/3.png', 'alt-attr-value', 'class-attr-value');

$form->input(_('My label'), 'attr-id-name');
$form->input(_('My label'), 'attr-id-name', 'attr-value', 'attr-class');
$form->text(_('My label'), 'attr-id-name', 'attr-value');
$form->file(_('My label'), 'attr-id-name', 'attr-value');

$form->confirmPassword = null;
$form->password(_('Password'), 'attr-id-name');

$form->confirmPassword = _('Confirm password');
$form->password(_('Password'), 'attr-id-name');

All arguments can be optional.

See Form API - shortcuts

Adding captcha

Make Simple captcha available to the application: Getting started tutorial - simple captcha

/* default   input id: captcha
   default captcha image URL: BASE_URL/captcha.jpeg */
$form->captcha(_('Enter what you see in the image'));

$form->captcha(
    _('Enter what you see in the image'),
    'my-id'
);

$form->captcha(
    _('Enter what you see in the image'),
    'my-id',
    BASE_URL.'captcha/random.jpg'
);

Adding widget

To add any custom HTML in a form row:

$form->addWidget(
    _('My label'),
    '<input id="my-widget" name="my-widget"/>',
    'my-widget-unique-name1'
);

$form->addWidget(
    null,
    _('Hi this is a message in the middle of the form'),
    'my-widget-unique-name2'
);

Grouping...

When elements are added to a form they will be rendered in separated lines. It is possible to put two or more elements in one line by method group:

$user = new User();
$form = new Form();

/* add model properties to form */
$form->add($user, 'email', 'password', 'name', 'active');

/* add buttons to form */
$form->button('Add',    'addUser();',   'button-add');
$form->button('Cancel', 'resetForm();', 'button-cancel');

/* 
 Form layout will be as:

Email:    [email input]
Password: [password input]
Name:     [name input]
Active:   [active input]
          |Add|
          |Cancel|
 */

/* put email and password in one line */
$form->group(null, array('email', 'password'), True);

/* put buttons in one line */
$form->group(null, array('button-add', 'button-cancel'));

/* 
 Form layout will be as:

Email:  [email input] Password: [password input]
Name:   [name input]
Active: [active input]
        |Add| |Cancel|
 */

First argument is the label for the group line. In the above example there will be no label.

Second argument is a list of model property names or element names/ids which will be grouped in one line.

The third argument is boolean, when False(default) the widgets will be placed beside each other in a line but when True the original label of each widget will be placed next to it.

Adding form tag and section

To insert form tags:

$form->addForm();

/* on submit call validate form */
$form->addForm('validateForm()'); 

/* second param is action */
$form->addForm('validateForm()', BASE_URL.'submit');

/* third param is method */
$form->addForm('validateForm()', BASE_URL.'submit', 'post');

/* first param can be array of form attributes */
$form->addForm($array(
    'action'   => BASE_URL.'submit',
    'method'   => 'post',
    'onsubmit' => 'return validateForm();';
));

/* close form tag */
$form->closeForm();

To divide form into sections using "fieldset" or "div" and "h3":

/* create Field set */
$form->addSection();

/* create Field set and legend */
$form->addSection('Legend label');

/* create Field set and legend */
$form->addSection('Legend label', array(/* fieldset attributes */));

/* create section with div and h3 instead of fieldset */
$form->addSection(
    'h3 text',
    array(/* fieldset attributes */),
    Form::DIVH3
);

/* close section */
$form->closeSection();

Attributes

After adding elements to a form, the form can be rendered as HTML. Beside the inputs and widgets other HTML entities such as table and labels are used to create the form layout, method attributes lets assigning attributes to all HTML entities which are used to render a form layout. By default the form layout has enough id and class attributes to be styled by CSS, but sometimes specific single elements need to be pointed out this is where method attributes comes handy.

Below is a general form layout rendered as HTML by class Form:

<table class="form-table">
  <tr>
    <td id="user-id-td-label" class="caption">
      <label id="user-id-label">ID</label>
    </td>
    <td id="user-id-td" >
      <input maxlength="10" class="int max4294967295 min0" value=""
             name="user-id" id="user-id" type="text" title="ID"/>
    </td>
  </tr>
  <tr>
    <td id="user-name-td-label" class="caption">
      <label id="user-name-label"></label>
    </td>
    <td id="user-name-td" >
      <label>Name</label>
      <input maxlength="45" class="text required" value="" name="user-name"
             id="user-name" type="text" title="Name"/>
      <label>Email</label>
      <input maxlength="255" class="email required" value="" name="user-email"
             id="user-email" type="text" title="Email"/>
    </td>
  </tr>
  <tr>
    <td id="user-password-td-label" class="caption">
      <label id="user-password-label">Password</label>
    </td>
    <td id="user-password-td" >
      <input maxlength="100" class="text password required" value=""
             name="user-password" id="user-password" type="password"
             title="Password"/>
    </td>
  </tr>
  <tr>
    <td id="user-slug-td-label" class="caption">
      <label id="user-slug-label">Slug</label>
    </td>
    <td id="user-slug-td" >
      <input maxlength="255" class="text required regex^[A-Za-z0-9\-]*$"
             value="" name="user-slug" id="user-slug" type="text" title="URL"/>
    </td>
  </tr>
  <tr>
    <td id="save-button-td-label" class="caption">
      <label id="save-button-label"></label>
    </td>
    <td id="save-button-td" >
      <button onclick="save();" name="save-button" type="button"
              id="save-button" title="">
        Save
      </button>
      <button onclick="cancel();" name="cancel-botton" type="button"
              id="cancel-botton" title="">
        Cancel
      </button>
    </td>
  </tr>
</table>

Above example shows how class Form renders and assigns id and class attributes. The class attribute for widgets include data which is used to validate widget value by Artaengine JavaScript class (arta.js). If arta.js is not used this classes can be eliminated from the form:

$form = new Form();
/* to eliminate class attribute from wigets */
$form->jsValidate = False;

Below example shows how to use method attributes to add attributes to form elements:

$user = new User();
$form = new Form();

/* attributes and params must set before adding elements to form*/
$form->attributes(array(
    /* change attributes of widget assigned to $user->name */
    'name' => array('class' => 'something'),

    /* change attributes of tr elements */
    'tr' => array(
        /* change attributes of tr holding widget for $user->name */
        'name' => array('onclick' => 'doSomething();'),
    ),

    /* change attributes of td elements */
    'td' => array(
        /* change attributes of td holding widget for $user->name */
        'name' => array('class' => 'td-widget-css'),
    ),

    /* change attributes of label elements */
    'label' => array(
        /* change attributes of label related to $user->name */
        'name' => array('class' => 'td-label-css'),
    ),

    /* change attributes of table element */
    'table' =>  array('id' => 'my-form'),
));

/* add elements to form */
$form->add($user);
$form->button('Save', 'save();', 'save-button');
$form->button('Cancel', 'cancel();', 'cancel-botton');
$form->group(null, array('name', 'email'), True);
$form->group(null, array('save-button', 'cancel-botton'));

Parameters

Some widgets used in a form accept parameters to method param accepts parameters and sends them to the widget renderer. See which widgets accept parameters.

$form->params(array(
    /* unit displayes a text after a widget */
    'age' => array('unit' => 'years old',),

    /* contol data of wigets which use database */
    'City' => array(
        // change data table or view
        'table' => "custom_city_view",
        // condition
        'where' => "active = 'Y'",
        // sort data
        'sort'  => 'country_name, city_name',
        // data column to be used as data id in the widget
        'id'    => 'city_id',
        // data column(s) to be used as show text in the widget
        'text'  => array('country_name', 'city_name'),
     ),
));

Note: class Form takes care of data sources for widgets related to models. Normally the only time a parameter needs to be set is to add a condition for limiting widget data.

Get raw form

Method raw returns form raw data in an array:

$raw = $form->raw();

$raw = array(
    'property name' => array(
        'label',
        'widget HTML',
        'type of widget or input',
        'unique name',
    ),
);
  • Array key is set to model property name or widget name (when widget is added not from model data).
  • Translated (when application is multi lingual) label to be shown before the widget.
  • Widget HTML tag or tags.
  • String showing the type of widget.
  • If widget has an id or name attribute it would be it otherwise it would be a unique string.

Render form as HTML

Method render renders and returns form as HTML:

/* RENDER AN ADD FORM */
$user = new User();
$form = new Form();
$form->add($user, User::addCols());
$form->button('Save', 'save();', 'save-button');
/* render form as HTML, suitable to be used in a template
   or sent by AJAX to be shown on the page */
$html = $form->render();

/* RENDER AN UPDATE FORM */
$user = new User();
$user->id = 7;
$user->query();
$form = new Form();
$form->add($user, User::updateCols());
/* render form as HTML, suitable to be used in a template
   or sent by AJAX to be shown on the page */
$html = $form->render();

See how a rendered form HTML looks like.

Method render accepts a parameter to change form style:

$html = $form->render();

  Label1  [widget1]
  Label2  [widget2]
  Label3  [widget3]


$html = $form->render(Form::FORM_LABEL_LAYOUT_V);

  Label1
  [widget1]
  Label2
  [widget2]
  Label3
  [widget3]
        

Filling model with posted data

After form data has been posted back from the browser it can be filled into the associated model object:

$model = new User();

/* fill model with $_POST data */
Form::bindTo($model);

/* fill model with $_GET data */
Form::bindTo($model, 'get');

/* fill model with $_POST data and add/update model data
   if isset($_POST['submit-button']) */
if (Form::bindTo($model, 'post', 'submit-button')) {
    if ($request->Post->has('update')) {
        $model->update()
    } else {
        $model->add();
    }
    $ok = $model->commit();  
}

If form has a name:

$model = new User();
$form  = new Form('myform');

/* fill model with $_POST data */
$form->bind($model);

/* fill model with $_GET data */
$form->bind($model, 'get');

/* fill model with $_POST data and add/update model data
   if isset($_POST['submit-button']) 
   if a token match is found */
if ($form->bind($model, 'post')) {
    if ($request->Post->has('update')) {
        $model->update()
    } else {
        $model->add();
    }
    $ok = $model->commit();
}

To fill a model with data from PHP $_POST or $_GET class Form uses class Binder.
See Binder class API

Validating form data

Data can be validated by Artaengine validators both on client and server.

Client side data validation

  • Include jQuery library and arta.js to the page.
  • Add message dictionary i18n/en.js to the page. This file contains error validate messages in English. edit to change the messages to what you want or translate to other languages and load correct dictionary based on language.
  • Set a message box so if there are errors this would pop and show the messages:
    <div id="msg-box" onclick="$(this).hide()" style="display:none"/>
    
  • AJAX: Set "Save" button "onclick" event to a function such as:
    function validateSendData() {
        /* validate data - if no problem returns data pack to be posted
           to server */
        var data = Arta.validate('#form');
        if (data === false) {
            /* validating errors - show message */
            return Arta.validateMsg();
        }
    
        /* no problem send data by AJAX */
        Arta.ajax('URL', data).success(function (oJ) {
            /* successfull */
        });
    }
    
  • Not AJAX: Set form "onsubmit" event to a function such as:
    /* store add/update form data */
    function validateData() {
      /* validate data */
      if (Arta.validate('#form') === false) {
        /* validating errors - show message and cancel submit */
        return Arta.validateMsg();
      }
      /* no problem continue submitting the form */
      return true;
    }
    

Server side data validation

$user = new User();
Form::bindTo($user);

/* create validate messages */
$errorMsg = array(
    Validate::REQUIRED         => 'required',
    Validate::NUMBER_SMALL     => 'number is too small',
    Validate::NUMBER_LARGE     => 'number is too large',
    Validate::INVALID_STRING   => 'invalid string',
    Validate::INVALID_INT      => 'invalid integer',
    Validate::INVALID_BOOL     => 'invalid boolean',
    Validate::INVALID_FLOAT    => 'invalid float',
    Validate::INVALID_ARRAY    => 'invalid array',
    Validate::INVALID_DATE     => 'invalid date',
    Validate::INVALID_TIME     => 'invalid time',
    Validate::INVALID_DATETIME => 'invalid date time',
    Validate::STRING_SHORT     => 'string is too short',
    Validate::STRING_LONG      => 'string is too long',
    Validate::FORMAT_IP        => 'invalid IP',
    Validate::FORMAT_URL       => 'invalid URL',
    Validate::FORMAT_EMAIL     => 'invalid email',
);

/* validate all model properties */
if ($errors=Validate::evaluate($user)) {
    foreach ($errors as $property => $propertyErrors) {
        foreach ($propertyErrors as $error) {
            /* do what is needed */
            echo $property.' - '.$errorMsg[$error].'<br/>';
        }
    }
}

Cross-site request forgery

To help preventing cross-site request forgery when a form is rendered a random token is added to a hidden input and put inside the session. All binding methods: Form.bind(), Form::b() and Bind::pull() can check if posted form data comes with a valid token or not, if not they will return false without binding any data.

The third argument of this three methods is an optional boolean value or a parameter array. true means check token, if a parameter array is passed then array('token' => true) means check token. false and array('token' => false) will turn of token checking.

Methods Form.bind() and Form::b() will check tokens unless the third argument is set to prevent token checking. But Bind::pull() does not check tokens by default because class Binder is a generic class for binding model data to other data sources and not only form data.

Example

Models - input form
TOP