Skip to content
Derek Jones edited this page Jul 5, 2012 · 28 revisions

Category:Library | Category:Library::Community | Category:Library::Forms

The Form library is a complete replacement for the Form helper. It reads an XML document and converts it to an array (via the Xml_Library).

Updates

2006-12-08:

  • New version, some API changes (options in dropdowns are reversed from previous order!)
  • Entire library has been cleaned up and optimized a bit
  • Now integrates with the Uploader library to allow the creation of upload forms

2006-11-06:

  • is no longer needed, validation rules are used to find required fields
  • Changed several bits, parse by fieldsets
  • Changed the format of form templates

2006-11-04:

  • Made validate() set the form values, no need to set them manually
  • Tweaked the valid types and attributes

Features

  • Built in validation using CI Validation
  • 1 call data retrieval
  • XHTML 1.0 output code
  • written for PHP5 (not PHP4 compatible)

Usage

$this->load->library('form');
$this->form->load('login'); // Relative to APPPATH/data/forms/, ".xml" appended
if ($data = $this->form->post_data()) {
  print_r($data);
}
else {
  print $this->form->build();
}

Library Code

<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

/***
 * Form library for CodeIgniter
 *
 *    author: Woody Gilk
 * copyright: (c) 2006
 *   license: http://creativecommons.org/licenses/by-sa/2.5/
 *      file: libraries/Form.php
 */

class Form {

    public function Form () {
    /***
     * @constructor
     */
        $obj =& get_instance();    
        $obj->load->library('xml');
        $this->ci =& $obj;  
    } /*** END ***/ 
    
    private $ci;

    /*** Public variables ***/
    public $error     = '';
    public $set_error = '';
    public $output    = '';

    /*** Internal variables ***/
    private $action;
    private $rules;
    private $upload_rules; 
    private $data;
    private $set_data;
    private $elements        = array();
    private $elements_upload = array();
    private $uploaded_files  = array();
    private $has_upload = false;

    public function load ($name) {
    /***
     * @public
     * Load a form definition for parsing
     */
        if (! $this->ci->xml->load ("data/forms/$name")) {
            $this->error = "Failed to load form: $name";
            return false; 
        }

        // Reset sensative vars to default value
        $this->action = '';
        $this->rules  = '';
        $this->data   = '';
        $this->set_data   = '';
        $this->set_error  = '';
        $this->has_upload = false;
        $this->elements   = array(); 

        $data = $this->ci->xml->parse ();
        if (! is_array($data)) {
            $this->error = "No form data found in /data/forms/$name.xml";
            return false;  
        }
        else {
            $data = $data['form'][0];

            $this->data     = $data;
            $this->action   = $data['__attrs']['action'];

            $this->get_fields ($data);

            $this->elements        = array_unique($this->elements);
            $this->elements_upload = array_unique($this->elements_upload); 
        }   

        return true;
    } /*** END load ***/  

    private function get_fields ($array) {
    /***
     * @private
     * Extract the rules and field names from the data
     */  
        if (! is_array($array)) {
            return;
        } 

        foreach ($array as $key => $val) {
               if ($key == 'fieldset' && is_array ($val)) {
                    foreach ($val as $_val) {
                    $this->get_fields ($_val);
                }
            }
            elseif (! is_numeric ($key) && $key != '__attrs' && $key != 'fieldset') {
                foreach ($val as $_key => $_val) {
                    if (isset ($_val['__attrs']) && $_attrs = $_val['__attrs']) {
                        if (isset ($_attrs['rules'])) {
                            $this->rules[$key] = $_attrs['rules'];
                           }
                          if (isset ($_attrs['allow'])) {
                            $this->upload_rules[$key] = $_attrs['allow'];
                        }
                          }

                    if (isset ($_val['type']) && $_val['type'][0] == 'file') {
                        $this->has_upload        = true;
                        $this->elements_upload[] = $key;
                    }
                    else {
                                $this->elements[] = $key;
                     }                            
                } // end foreach
            } //end elseif   
        } 
    } /*** END get_fields ***/   

    public function set_action ($location) {
    /***
     * @public
     * Set the form action
     */  

        $this->action = $location;
        return true;
    } /** END action ***/  

    public function set ($element, $key, $value = false) {
    /***
     * @public
     * Set an attribute of an element, or a new elemenet
     */  
        if (is_array ($key)) {
            foreach($key as $_key => $_value) {
                $this->set($element, $_key, $_value);
            }

            return true;
        }  

        $this->set_data[$element][$key] = $value;
        return true; 
    } /*** END set ***/

    public function post_data ($validate = true) {
    /***
    * @public
    * Return all the post data from the loaded form
    */ 
        if ($validate == true && ! $this->validate ()) {
            return false;
        }
        if (@ count($this->elements) < 1) {
            return false;
        }  

        $data  = array ();
        foreach ($this->elements as $elem) {
            $data[$elem] = stripslashes ($this->ci->input->post ($elem));
        } 

        return array_merge($data, $this->uploaded_files); 
    } /*** END post_data ***/

    private function do_uploads() {
        /***
         * @private 
         * Helper function for validation
         */
        $error = false;
        $data  = array();
        if (count ($this->elements_upload) > 0) {
            $config['upload_path']   = isset ($this->upload_path) ? $this->upload_path : './upload/';
            $config['remove_spaces'] = true;
            $config['xss_clean']     = true;
            $config['max_size']      = '2048'; 

            $this->ci->load->library ('upload');

            foreach ($this->elements_upload as $elem) {
                // Reset the configuration
                if (is_array ($this->upload_rules) && isset($this->upload_rules[$elem])) {
                    $config['allowed_types'] = $this->upload_rules[$elem];
                }

                if (isset ($this->rules[$elem]) && $rules = $this->rules[$elem]) {
                    $required = strpos ($rules, 'required') !== false ? true : false;
                }
                else {
                    $required = false;
                }
                    

                $this->ci->upload->initialize ($config);

                if ($this->ci->upload->do_upload ($elem)) {
                    $return = $this->ci->upload->data();
                    $this->uploaded_files[$elem] = $return;
                }
                elseif ($required == true) {
                    $error = true;
                
                    $errors = $this->ci->upload->display_errors();
                    $this->set_error .= $errors;
                }
            }
        }

        return ($error == true ? false : true);
    } /* END do_uploads */

    public function validate ($name = false) {
    /***
     * @public
     * Validates a form based on the rules found in the definition
     */    
        if ($name != false && ! $this->load ($name)) {
            return false;
        }
        elseif (! is_array ($this->rules)) {
            if (count ($this->elements_upload) > 0) {
                return $this->do_uploads();
            }

            return false;
        } 

        $this->ci->load->library ('validation');
        $this->ci->validation->set_rules ($this->rules);

        if ($this->ci->validation->run() && $this->do_uploads())  {
             return true;
        }
        else {
            foreach ($this->post_data (false) as $key => $val) {
                // Set default values
                $this->set($key, 'value', $val);
            }   
            
            foreach ($this->ci->validation->_error_array as $error) {
                $this->set_error .= "\t<p>$error</p>\n";
            }  
        }

        return false;
    } /*** END validate ***/  

    private function build_group ($group, $depth = 0) {  
    /***
     * @private
     * Build a fieldset
     */
        static $first_run;
        static $tabindex;
        
        $tabindex = isset ($tabindex) ? $tabindex : 1; 

        // Set the valid attributes and type values
        $valid_attr = array(
            'type', 'maxlength', 'value', 'options',
            'selected', 'checked', 'rows', 'cols', 'size',
            'onclick', 'onmouseover', 'onmouseout', 'onchange' 
        ); 
        $valid_type = array(
            'text', 'textarea', 'password', 'file',
            'dropdown', 'radio', 'checkbox',
            'submit', 'button', 'hidden' 
        ); 
        $tabs = repeater("\t", $depth);

        $html  = sprintf ("$tabs".'<fieldset><legend>%s</legend>'."\n", $group['__attrs']['name']);
        if ($first_run !== false) {
            $errors = '<div class="error message">'."\n\t". $this->set_error ."\n\t</div>";
            $html .= "$tabs". preg_replace("|\n\t+|", "\n$tabs\t", $errors) ."\n";
            $first_run = false; 
        }
        if (isset ($group['__attrs']['text']) && $text = $group['__attrs']['text']) {
            $html .= "$tabs\t<p>$text</p>\n";
        } 

        $html .= "$tabs\t".'<ol class="layout">'."\n";

        foreach ($group as $name => $val) {
            if ($name == '__attrs') {
                continue;
            } 
            elseif ($name == 'fieldset') {
                foreach ($val as $_group) {
                    $html .= "$tabs\t<li>\n". $this->build_group ($_group, $depth+2) ."$tabs\t</li>\n";
                } 
            } 
            else {
                foreach ($val as $index => $def) {
                    foreach ($def as $key => $val) {
                        if ($key == '__attrs') {
                            unset ($def[$key]);
                            continue;
                        }

                        $def[$key] = $val[0];
                    }

                    // Skips defs that have no type attribute
                    if (! isset($def['type']) || ! in_array ($def['type'], $valid_type)) {
                        continue;
                    }

                    // Externally set data is present, merge with stored efinition
                    if (isset ($this->set_data[$name]) && is_array ($this->set_data[$name])) {
                        if ($def['type'] == 'checkbox' && isset ($this->set_data[$name]['value'])) {
                            $this->set_data[$name]['checked'] = (bool)$this->set_data[$name]['value'];
                            unset ($this->set_data[$name]['value']);    
                        }
                        elseif ($def['type'] == 'submit' && isset ($this->set_data[$name]['value'])) {
                            unset ($this->set_data[$name]['value']);    
                        }

                        $def = array_merge($def, $this->set_data[$name]);
                    }

                    // We always want a default value
                    $def['value'] = isset($def['value'])
                        ? $def['value'] : '';

                    // Choose a label
                    $label = isset($def['label'])
                         ? ucwords($def['label'])
                        : ucwords($name);

                    // Create the id and name attributes
                    $idname = $def['type'] != 'hidden'
                        ? sprintf('tabindex="%s" name="%s"', $tabindex++, $name)
                        : sprintf('name="%s"', $name);

                    // Add "*" on required items
                    $label = isset($this->rules[$name]) && in_array('required', explode('|', $this->rules[$name]))
                        ? "$label <em>*</em>"
                        : $label; 

                    $row  = "";
                    $row .= $def['type'] != 'hidden'
                        ? "$tabs\t<li>\n"
                        : '';

                    $row .= $def['type'] != 'submit' && $def['type'] != 'hidden'
                        ? "$tabs\t\t<label>$label</label>\n" : '';

                    // Handle non-input elements
                    switch ($def['type']) {
                    case 'textarea':
                        $input  = "$tabs\t\t&lt;textarea $idname %s&gt;". $def['value'] ."&lt;/textarea&gt;\n";
                        unset ($def['type'], $def['value']);
                        break;  
                    case 'dropdown':
                        $def['value'] != false && $def['selected'] = $def['value'];
                        unset ($def['value']);

                        if (isset ($def['options'])) {
                            $options = '';
                            foreach ($def['options'] as $_key => $_val) {
                                $_val = is_array ($_val) ? $_val[0] : $_val; 

                                $sel = isset ($def['selected']) && $def['selected'] == $_key
                                    ? ' selected="selected"' : '';

                                $options .= "$tabs\t\t\t<option value=\"$_key\"$sel>$_val$tabs</option>\n";
                            }
                            unset ($def['type'], $def['options'], $def['selected']);

                            $input = "$tabs\t\t<select $idname %s >\n$options$tabs\t\t</select>\n";
                        }
                        else {
                            continue(2);
                        }
                        break;
                    case 'hidden':
                        $input = "$tabs\t&lt;input $idname %s style=\"display:none;\" /&gt;\n";
                        break;
                    default:
                        $input = "$tabs\t\t&lt;input $idname %s /&gt;\n"; 
                    }     

                    // Parse attributes
                    $attributes = '';
                    foreach ($def as $attr => $val) {
                        if (in_array ($attr, $valid_attr)) {
                            if ($attr == 'checked' && $val != false) {
                                $val = 'checked';
                            }
                            elseif ($attr == 'checked') {
                                continue;
                            }

                            $attributes .= " $attr=\"$val\" ";
                        }
                    } 
            
                    $row .= sprintf($input, $attributes);
                     $row .= isset($def['type']) && $def['type'] == 'hidden'
                        ? ''
                        : "$tabs\t</li>\n";

                    $html .= "$row";
                }
            }             
        }  

        $html .= "$tabs\t".'</ol>'."\n";
        $html .= "$tabs".'</fieldset>'."\n";

        return $html; 
    } /* END build_group */

    public function build ($name = false) {
    /***
     * @public
     * Convert a form definition into an XHTML form
     */
        if ($name != false && ! $this->load ($name)) {
            return false;
        }
        elseif (! is_array ($this->data)) {
            return false;
        }  
    
        $this->ci->load->helper('string');

        $form_type = $this->has_upload == true
            ? ' enctype="multipart/form-data"'
            : ''; 

        $out =& $this->output;
        $out  = '';
        $out .= sprintf("".'&lt;form action="%s" id="%s" method="post"%s&gt;'."\n",
            site_url($this->action),
            strtolower(preg_replace('|\W|', '_', $this->data['__attrs']['name'])),
            $form_type
        ); 

        foreach ($this->data['fieldset'] as $group) {
            $out .= $this->build_group($group);
        }

        $out .= "&lt;/form&gt;\n";

        $this->output = $out;
        return $out;   
    } /*** END build ***/ 

}

?&gt;

Sample Form:

&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;form action="user/login" name="user_login"&gt;
  <fieldset name="User Login">
    <username rules="trim|required|min_length[4]|max_length[32]|xss_clean">
      <type>text</type>
      <maxlength>32</maxlength>
    </username>
    <password rules="required|min_length[4]|max_length[32]|xss_clean">
      <type>password</type>
      <maxlength>32</maxlength>
    </password>
    <fieldset name="Submit">
      <submit>
        <type>submit</type>
        <value>Login</value>
      </submit>
    </fieldset>
  </fieldset>
&lt;/form&gt;
Clone this wiki locally