Part of the EllisLab Network
x
 
Create New Page
 View Previous Changes    ( Last updated by derekmichaeljohnson )

Auto Form Fields

If you’re like me, and you hate having to create forms by hand for every new project, you might appreciate this helper.  It automatically generates your form fields based on your table fields.  It even checks for lookup and bridge tables that are related to your form.

As of right now this only supports MySQL, but by no means is it a finished product.  And undoubtedly you guys can think of a hundred ways to improve upon it.

IMPORTANT: The helper currently expects bridge tables (for many-to-many relationships) to end in “_bridge”. So, for users_movies, the bridge table should be called users_movies_bridge.  I’m not saying this is the best naming convention, it’s just what I happen to use.

Sample database tables (used in example usage below):

CREATE TABLE IF NOT EXISTS `users` (
  `
idint(11NOT NULL AUTO_INCREMENT,
  `
activetinyint(1NOT NULL DEFAULT '1',
  `
groupint(11NOT NULL DEFAULT '10',
  `
role_idint(11) DEFAULT NULL,
  `
emailvarchar(255) DEFAULT NULL,
  `
passvarchar(128) DEFAULT NULL,
  `
fnamevarchar(64) DEFAULT NULL,
  `
abouttext,
  `
datetimestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  
PRIMARY KEY (`id`)
ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=;

CREATE TABLE IF NOT EXISTS `users_roles` (
  `
idint(11NOT NULL AUTO_INCREMENT,
  `
namevarchar(255) DEFAULT NULL,
  
PRIMARY KEY (`id`)
ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=;

INSERT INTO `users_roles` (`id`, `name`) VALUES
(1'Board of Directors'),
(
2'President'),
(
3'Vice President'),
(
4'Director'),
(
5'Manager');

CREATE TABLE IF NOT EXISTS `users_tv_shows` (
  `
idint(11NOT NULL AUTO_INCREMENT,
  `
namevarchar(128) DEFAULT NULL,
  
PRIMARY KEY (`id`)
ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=;

INSERT INTO `users_tv_shows` (`id`, `name`) VALUES
(1'Seinfeld'),
(
2'Curb Your Enthusiasm'),
(
3'The Office'),
(
4'Fringe'),
(
5'Lost');

CREATE TABLE IF NOT EXISTS `users_tv_shows_bridge` (
  `
idint(11) DEFAULT NULL,
  `
uidint(11) DEFAULT NULL
ENGINE=MyISAM DEFAULT CHARSET=latin1

Example usage:

// Controller

$this->load->helper('form');
$data['user'$this->db->where('id',1)->get('users');
$this->load->view('form_view',$data);

// View

<?=form_open()?>
<?
=form_auto_fields('users',$user)?>
<?
=form_close()?> 

And the helper:

function form_auto_fields($table null$db_record null$exceptions = array(), $vocab = array())
{
    
// table name required
    
if(!is_null($table))
    
{
        
// get CI super object
        
$ci =& get_instance();

        
// load 'information_schema' database
        
$ci->info_db $ci->load->database('info_sch'TRUE);
        
        
// retrieve field comments, and simplify array (this is for future usage of field comments, not currently implemented)
        
$info_query $ci->info_db->select('column_name, column_comment')->where('table_name',$table)->get('columns');
        
$comments = array();
        foreach(
$info_query->result_array() as $info_field)
        
{
           
           $comments[$info_field[
'column_name']] $info_field['column_comment'];
        
}
        
        
// retrieve all table fields
        
$ci->db $ci->load->database('default'TRUE);
        
$q $ci->db->query("DESCRIBE `$table`");
        
        
// default EXCEPTIONS
        // is merged with passed $exceptions array
        
$default_exceptions = array(
            
'group',
            
'add_date',
            
'exp_date'
        
);
        
$exceptions = (!empty($exceptions) AND is_array($exceptions)) ? array_merge($exceptions,$default_exceptions) : $default_exceptions;
        
        
// default VOCAB list
        // is merged with passed $vocab array
        
$default_vocab = array(
            
'email' => 'Email',
            
'pass' => 'Password',
            
'fname' => 'First Name',
            
'lname' => 'Last Name',
            
'addr' => 'Address',
            
'city' => 'City',
            
'st' => 'State',
            
'zip' => 'Zip',
            
'phone' => 'Phone',
            
'about' => 'About'
        
);
        
$vocab = (!empty($vocab) AND is_array($vocab)) ? array_merge($vocab,$default_vocab) : $default_vocab;
        
        
// db record
        
if(!is_null($db_record))
        
{
            $db_record 
$db_record->row_array();
        
}
        
        
// create new column object and default variables
        
$col = new StdClass;
        
$count 0;
        
$fields = array();
        
$form_multipart false;
        
$joined_tables = array();
            
        foreach(
$q->result() as $field)
        
{
            
// get field name, type, key, auto_incrementing and comments
            
$col->name $field->Field;
            
$col->type preg_replace('/\(.+?$/',"",$field->Type);
            
$col->primary = ($field->Key == 'PRI') ? 0;
            
$col->auto_increment = ($field->Extra == 'auto_increment') ? 0;
            
$col->comments $comments[$col->name];
            
            
// get field length
            
preg_match('/\((.*?)\)/'$field->Type$matches);
            
$col->length = (isset($matches[1])) ? $matches[1] '';
            
            
// default variables
            // which need to be reset with each loop
            
$field_output '';
            
$short_name '';
            
            
// get field's db record (if passed)
            
$field_db_record = (isset($db_record[$col->name])) ? $db_record[$col->name] '';
            
            
// exclude EXCEPTIONS, primary keys, auto_incrementing fields, timestamps
            
if($col->primary == AND $col->auto_increment == AND $col->type != 'timestamp' AND !in_array($col->name,$exceptions))
            
{
                
if($col->type == 'varchar')
                
// VARCHAR
                
{
                    $field_output 
.= '<input type="'.($col->name == 'pass' 'password' 'text').'" name="'.$col->name.'" value="'.$field_db_record.'" class="" />';
                
}
                
elseif($col->type == 'text')
                
// TEXT
                
{
                    $field_output 
.= '<textarea name="'.$col->name.'" class="">'.$field_db_record.'</textarea>';
                
}
                
elseif($col->type == 'int')
                
// INTEGER
                
{
                    $short_name 
rtrim($col->name,'_id');
                    
$join_table false;
                    
                    
// check if field has corresponding table
                    // example: cat_id will have table "cats" or "{$table}_cats"
                    
if($ci->db->table_exists($short_name.'s'))
                    
{
                        $join_table 
$short_name.'s';
                    
}
                    
elseif($ci->db->table_exists($table.'_'.$short_name.'s'))
                    
{
                        $join_table 
$table.'_'.$short_name.'s';
                    
}
                    
elseif($ci->db->table_exists($table.'_'.$short_name))
                    
{
                        $join_table 
$table.'_'.$short_name;
                    
}
                    
                    
// if table found, create dropdown
                    
if($join_table)
                    
{
                        $joined_tables[] 
$join_table// this is used AFTER the loop to exlude this table when looking for lookup tables
                        
                        
$q $ci->db->get($join_table);
                        
$field_output .= form_dropdown($col->name,$q,$field_db_record);
                    
}
                    
// otherwise, create input field
                    
else
                    
{
                        $field_output 
.= '<input type="text" name="'.$col->name.'" value="" class="numeric" />';
                    
}
                }
                
elseif($col->type == 'tinyint' AND $col->length == 1)
                
// TINYINT (bool)
                
{
                    $field_output 
.= "<ul>\n";
                    
$field_output .= '<li><input type="radio" name="'.$col->name.'" value="1" class="checkbox"'.($field_db_record === '1' ' checked="checked"' '').' /><span>Yes</span></li>'."\n";
                    
$field_output .= '<li><input type="radio" name="'.$col->name.'" value="0" class="checkbox"'.($field_db_record === '0' ' checked="checked"' '').' /><span>No</span></li>'."\n";
                    
$field_output .= "</ul>\n";
                
}
                
elseif($col->type == 'decimal' OR $col->type == 'float')
                
// DECIMALS and FLOATS
                
{
                    
                }
                
elseif($col->type == 'date' OR $col->type == 'timestamp')
                
// DATES and TIMESTAMPS
                
{
                    $field_output 
.= '<input type="text" name="'.$col->name.'" value="'.$field_db_record.'" class="date" />';
                
}
                
                
// after all that, if field has been created, build final <li> output
                
if($field_output != '')
                
{
                    $fields[$count] 
"<li>\n";
                    
$fields[$count] .= '<label>';
                    if(
$short_name != '')
                    
{
                        $fields[$count] 
.= ucwords($short_name);
                    
}
                    
elseif(isset($vocab[$col->name]))
                    
{
                        $fields[$count] 
.= $vocab[$col->name];
                    
}
                    
else
                    
{
                        $fields[$count] 
.= ucwords($col->name);
                    
}
                    $fields[$count] 
.= "</label>\n";
                    
$fields[$count] .= "<div>\n".$field_output."\n</div>\n";
                    
$fields[$count] .= "</li>\n";
                    
                    
$count++;
                
}
            }
        }
        
        
// before we wrap this up, let's check for
        // lookup tables related to this form
        
$tables $ci->db->list_tables();
        foreach(
$tables as $t)
        
{
            
// related tables will include original table's name
            // but we obviously want to exclude the original table,
            // already joined tables and bridge tables
            
if(strpos($t,$table) !== FALSE AND $t != $table AND !in_array($t$joined_tables) AND strpos($t,'_bridge') === FALSE)
            
{
                $lookup_field_output 
"<ul>\n";
                
$q $ci->db->get($t);
                if(
$q->num_rows() > 0)
                
{
                    $bridged 
= array();
                            
                    
// if db record passed, check bridge table for relational data
                    
if(isset($db_record['id']) AND $ci->db->table_exists($t."_bridge"))
                    
{
                        
// get bridged records
                        
$bridge $ci->db->where($table[0]."id",$db_record['id'])->get($t."_bridge");
                        if(
$bridge->num_rows() > 0)
                        
{
                            $bridged 
= array();
                            foreach(
$bridge->result() as $b)
                            
{
                                $bridged[] 
$b->id;
                            
}
                        }
                    }
                    
                    
foreach($q->result() as $cb)
                    
{
                        $checked 
= (in_array($cb->id,$bridged)) ? 'checked="checked" ' '';
                        
$lookup_field_output .= '<li><input type="checkbox" value="'.$cb->id.'" class="checkbox" '.$checked.'/><span>'.$cb->name.'</span></li>'."\n";
                    
}
                    $lookup_field_output 
.= "</ul>\n";
                
                    
$fields[$count] "<li>\n";
                    
$fields[$count] .= "<label>".ucwords(str_replace('_',' ',ltrim(ltrim($t,$table),'_')))."</label>\n";
                    
$fields[$count] .= "<div>\n".$lookup_field_output."\n</div>\n";
                    
$fields[$count] .= "</li>\n";
                
                    
$count++;
                
}
            }
        }
        
        
// ok, we're done
        
$output "\n<ul>\n";
        
$output .= implode("\n",$fields);
        
$output .= "\n".'<li class="btnHolder"><input type="submit" value="Submit" /></li>'."\n";
        
$output .= '</ul>';
        
        return 
$output;
        
    
}

}

// Modified form_dropdown() helper function

function form_dropdown($name ''$options = array(), $selected = array(), $extra '')
{
    
if ( ! is_array($selected))
    
{
        $selected 
= array($selected);
    
}

    
if ($extra != ''$extra ' '.$extra;

    
$multiple = (count($selected) > && strpos($extra'multiple') === FALSE) ? ' multiple="multiple"' '';

    
$form '<select name="'.$name.'"'.$extra.$multiple.">\n";
    
    
// check if $options is an object, if not, proceed with array
    
if(!is_object($options))
    
{
        
// create first blank option
        
$form .= '<option value=""></option>'."\n";
        
        foreach (
$options as $key => $val)
        
{
        
            $key 
= (string) $key;
        
            if (
is_array($val))
            
// optgroup (multidimensional array passed)
            
{
                $form 
.= '<optgroup label="'.$key.'">'."\n";

                foreach (
$val as $optgroup_key => $optgroup_val)
                
{
                    $sel 
= (in_array($optgroup_key$selected)) ? ' selected="selected"' '';

                    
$form .= '<option value="'.$optgroup_key.'"'.$sel.'>'.(string) $optgroup_val."</option>\n";
                
}

                $form 
.= '</optgroup>'."\n";
            
}
            
// no optgroup
            
else
            
{
                $sel 
= (in_array($key$selected)) ? ' selected="selected"' '';

                
$form .= '<option value="'.$key.'"'.$sel.'>'.(string) $val."</option>\n";
            
}
        }
    }
    
// $options is an object (likely a DB object)
    
else
    
{
        
// create first blank option element
        
$form .= '<option value=""></option>'."\n";
        
        foreach (
$options->result_array() as $row)
        
{
            
/**
             * need to get first 2 values in row's associative array.
             * in order to do so we must first get the keys of the array,
             * and use them later to reference numeric positions.
             */
            
$row_keys array_keys($row);
            
            
// check if the first value in the array (likely the row id) is in the $selected array
            
$sel = (in_array($row[$row_keys[0]]$selected)) ? ' selected="selected"' '';
            
            
$option_value $row[$row_keys[0]];
            
$option_display $row[$row_keys[1]];
            
            
$form .= '<option value="'.$option_value.'"'.$sel.'>'.$option_display."</option>\n";
        
}
    }

    $form 
.= '</select>';

    return 
$form;