Writing is liberating, writing is a way of learning, writing provokes endorphins. Therefore, I like to write sometimes.

Forms in CodeIgniter Views

Friday, November 23rd, 2007   by Favio

When you hand code your (x)HTML and, in addition, your CSS, Javascript, and PHP code; things start to look boring. Very Boring! Repetition is FTL. So we, coders, are always on the look for items, techniques, paradigms, frameworks, etc. that reduce the water in our systems. In other words, make us DRY.

From the perspective of an XHTML hand-coder and CodeIgniter user, a form generator *could* be useful in the sense of doing more in less time. This is a simple passive code generator. It takes into account what you would usually put in an "Add", or "Edit" form. The point here is to write a bit of information, and then, modify the result.

For Example

For a contact a form you could use the following CSV file:

name, text, Your Name, alpha
email, , Your Email, required|valid_email
message, textarea, Your Message, required

The output code, and browser display (with bare CSS) can be seen in the following screenshot:
screenshot of a form generator

The first "layout" is table based, and the second is from a Fancy Form CSS.

Sadly, my current (sponsored) host, does not support PHP5 yet! >_< So grab a copy of the source code, and put it in your sandbox to see it live.

Additionally, you can see the code right here:

PHP:
  1. <?php
  2. /**
  3. * Simple Form Generator
  4. *
  5. * A passive code generator for the html required to display forms, plus
  6. * the validation rules and logic of the CodeIgniter Validation library.
  7. *
  8. * COULD DO:
  9. * Save file dialog and preview
  10. * static selects
  11. * set value of radio buttons?
  12. * more layouts
  13. *
  14. * @author Favio Manriquez <favio@favrik.com>
  15. */
  16. class FormGenerator
  17. {
  18.     public  $Fields       = array();
  19.     private $Data;
  20.     private $Repopulate   = true;
  21.     private $Editing      = false;
  22.     private $InlineErrors = true;
  23.     private $RulePadding  = 0;
  24.     private $Rules;
  25.  
  26.     function __construct($data)
  27.     {
  28.         $this->Data = $data;
  29.     }
  30.  
  31.     public function TableLayout($tab = 0)
  32.     {
  33.         $this->processFields();
  34.         $output = "\n" . $this->spacer($tab) . "<table>\n";
  35.         $colspan = '';
  36.         foreach ($this->Fields as $f) {
  37.             if ($f->type == 'checkbox' or $f->type == 'radio') {
  38.                 $colspan = ' colspan="2"';
  39.                 $row = $f->type == 'checkbox' ?
  40.                          $this->spacer(2 + $tab) . "<td class=\"checkbox\">$f->field $f->label<span>$f->error</span></td>\n"
  41.                        :   $this->spacer(2 + $tab) . "<th>$f->label</th>\n"
  42.                          . $this->spacer(2 + $tab) ."<td>$f->field<span>$f->error</span></td>\n";
  43.             } else {
  44.                 $row  = $this->spacer(2 + $tab) . "<th>$f->label</th>\n";
  45.                 $row .= $this->spacer(2 + $tab) . "<td>$f->field<span>$f->error</span></td>\n";
  46.             }
  47.             $output .= $this->spacer(1 + $tab) . "<tr$colspan>\n";
  48.             $output .= $row;
  49.             $output .= $this->spacer(1 + $tab) . "</tr>\n";
  50.         }
  51.         $output .= $this->spacer($tab) . '</table>';
  52.         return  $output;
  53.     }
  54.  
  55.     public function FancyFormLayout($tab = 0)
  56.     {
  57.         $this->processFields();
  58.         $output = "\n" . $this->spacer($tab) . "<fieldset>\n<ol>\n";
  59.         foreach ($this->Fields as $f) {
  60.             $output .= $this->spacer(1 + $tab) . "<li>\n";
  61.             $output .= $this->spacer(2 + $tab) . $f->label . $f->field . "\n";
  62.             $output .= $this->spacer(1 + $tab) . "</li>\n";
  63.         }
  64.         $output .= $this->spacer($tab) . "</ol>\n</fieldset>\n";
  65.         return $output;
  66.     }
  67.  
  68.     public function Rules()
  69.     {
  70.         return "<?php\n".$this->Rules."?>\n";
  71.     }
  72.    
  73.     public function getLabelLayout() {}
  74.    
  75.  
  76.     public function repopulate($status)
  77.     {
  78.         $this->Repopulate = $status;
  79.         return $this;
  80.     }
  81.  
  82.     public function editing($status)
  83.     {
  84.         $this->Editing = (boolean) $status;
  85.         return $this;
  86.     }
  87.    
  88.     public function inlineErrors($status)
  89.     {
  90.         $this->InlineErrors = $status;
  91.         return $this;
  92.     }   
  93.  
  94.     public function spacer($space = 1)
  95.     {
  96.         return str_repeat(' ', $space * 4);
  97.     }
  98.  
  99.     private function processFields()
  100.     {
  101.         $this->Fields = array();
  102.         $previous = new stdClass();
  103.         $lines = explode("\n", $this->Data);
  104.         foreach ($lines as $line) {
  105.             $line = trim($line);
  106.             if (empty($line)) {
  107.                 continue;
  108.             }
  109.             $field      = array_map('trim', explode(',', $line));
  110.             $F          = new stdClass();
  111.             $F->name    = array_shift($field);
  112.             $F->type    = $this->fetchProperty($field, $previous->type);
  113.             $F->display = array_shift($field);
  114.             $F->rules   = array_shift($field);
  115.             $F->special = array_shift($field);
  116.             $previous   = clone $F;
  117.             $this->setPaddingForRules(strlen($F->name));
  118.             $this->setField($F);
  119.         }
  120.         $this->setRules();
  121.     }
  122.    
  123.     private function setPaddingForRules($length)
  124.     {
  125.         if ($length> $this->RulePadding) {
  126.             $this->RulePadding = $length;
  127.         }
  128.     }
  129.  
  130.  
  131.     private function fetchProperty(&$field, $previousValue = '')
  132.     {
  133.         $property = array_shift($field);
  134.         if (is_null($property) or empty($property)) {
  135.             return $previousValue;
  136.         }
  137.         return $property;
  138.     }
  139.  
  140.     private function setField($field)
  141.     {
  142.         $inputs = array('text', 'password', 'hidden',
  143.                         'file', 'checkbox', 'radio');
  144.         if (in_array($field->type, $inputs)) {
  145.             $field->field = $this->input($field);
  146.         } else {
  147.             $field->field = $this->{$field->type}($field);
  148.         }
  149.         $field->label = $this->setLabel($field);
  150.         $field->error = $this->InlineErrors ? $this->setError($field) : ''
  151.         $this->Fields[] = $field;
  152.     }
  153.    
  154.  
  155.  
  156.     private function setLabel($field)
  157.     {
  158.         return $this->label($field);
  159.     }
  160.  
  161.     private function setRules()
  162.     {
  163.         $rules = '';
  164.         foreach ($this->Fields as $F) {
  165.             $rules .= $this->setRule($F);
  166.         }
  167.         $rules .= '$this->validation->set_rules($rules);' . "\n";
  168.         $rules .= '$this->validation->set_fields($fields);' . "\n";
  169.         $rules .= '$this->validation->set_error_delimiters(\'<div class="error">\',\'</div>\');' . "\n";
  170.         $this->Rules = $rules;
  171.     }
  172.  
  173.     private function setRule($field)
  174.     {
  175.         $base_pad = 13; // 12 for $rules reflectd on the output string
  176.         $max      = $base_pad + $this->RulePadding;
  177.         $current  = $base_pad + strlen($field->name);
  178.         $total    = $max - $current;
  179.         $Padding  = str_repeat(' ', $total + 1);
  180.  
  181.         $post_rules = empty($field->rules) ? '' : '|';
  182.         return "\$rules ['$field->name']$Padding= 'trim|$field->rules{$post_rules}xss_clean';\n\$fields['$field->name']$Padding= '$field->display';\n";
  183.  
  184.     }
  185.  
  186.     private function setError($field)
  187.     {
  188.         return '<?php echo $this->validation->'. $field->name . '_error; ?>';
  189.     }
  190.  
  191.     private function label($field)
  192.     {
  193.         $class = strpos($field->rules, 'required') !== false
  194.                  ? ' class="required"' : '';
  195.         $colon = ':';
  196.         if ($field->type == 'radio' or $field->type == 'checkbox') {
  197.             $colon = '';
  198.         }
  199.         return '<label for="'.$field->name.'"'. $class .'>'.$field->display.$colon.'</label>';
  200.     }
  201.  
  202.     private function setClass($field)
  203.     {
  204.         return "class=\"$field->type\" ";
  205.     }
  206.  
  207.     /**
  208.      * Possible field types: text, password, hidden, textarea, select,
  209.      * checkbox, radio, file.
  210.      *
  211.      *
  212.      */   
  213.     private function input($field)
  214.     {
  215.         if ($field->type == 'checkbox') {
  216.             $value = " value=\"1\" " . $this->setCheckboxValue($field);
  217.         } else {
  218.             $value = ' value="' . $this->setValue($field).'"';
  219.         }
  220.         return '<input '.$this->setClass($field).'id="'.$field->name.'" type="'.$field->type.'" name="'.$field->name.'" '.$value.' />';
  221.     }
  222.    
  223.     private function select($field)
  224.     {
  225.         return '<select '.$this->setClass($field).'id="'.$field->name.'" name="'.$field->name.'"><option>--Select--</option>'
  226.                 .$this->setSelectValue($field).'</select>';
  227.     }
  228.  
  229.     private function textarea($field)
  230.     {
  231.         return '<textarea '.$this->setClass($field).'id="'.$field->name.'" name="'.$field->name.'" rows="5" cols="40">'.$this->setValue($field).'</textarea>';
  232.     }
  233.    
  234.     private function setValue($field)
  235.     {
  236.         if ($this->Repopulate) {
  237.             $v = $this->Editing
  238.                  ? "<?php echo (\$this->validation->{$field->name}) ? (\$this->validation->{$field->name}) : \$editing->{$field->name}; ?>"
  239.                  : '<?php echo $this->validation->' . $field->name . '; ?>';
  240.             return $v;
  241.         }
  242.         return '';
  243.     }
  244.    
  245.     private function setSelectValue($field)
  246.     {
  247.         if ($this->Repopulate) {
  248.             $selected = "<?php if (\$this->validation->{$field->name} == \$option->value) { echo 'selected=\"selected\"';} ?>";
  249.             if ($this->Editing) {
  250.                 $selected = "<?php if (\$this->validation->{$field->name} == \$option->value or \$editing->{$field->name} == \$option->value) { echo 'selected=\"selected\"';} ?>";
  251.             }
  252.             $php  = "<?php foreach (\$$field->special as \$option): ?>";
  253.             $php .= "<option $selected value=\"<?php echo \$option->value; ?>\"><?php echo \$option->text;?></option>";
  254.             $php .= "<?php endforeach; ?>";
  255.             return $php;
  256.         }
  257.     }
  258.  
  259.     private function setCheckboxValue($field)
  260.     {
  261.         if ($this->Repopulate) {
  262.             $v = $this->Editing
  263.                  ? "<?php echo (\$this->validation->set_checkbox('$field->name', '1')) ? (\$this->validation->set_checkbox('$field->name', '1')) : \$checked; ?>"
  264.                  : "<?php echo \$this->validation->set_checkbox('$field->name', '1'); ?>";
  265.             return $v;
  266.         }
  267.     }
  268. }
  269. ?>
  270. <!-- OMFG no Doctype!!! -->
  271. <html>
  272. <head>
  273. <title>CodeIgniter Formgen</title>
  274. <style type="text/css">
  275. fieldset { 
  276. margin: 1.5em 0 0 0
  277. padding: 0;
  278. }
  279. legend { 
  280. margin-left: 1em; 
  281. color: #000000; 
  282. font-weight: bold;
  283. }
  284. fieldset ol { 
  285. padding: 1em 1em 0 1em; 
  286. list-style: none;
  287. }
  288. fieldset li { 
  289. padding-bottom: 1em;
  290. }
  291. fieldset.submit { 
  292. border-style: none;
  293. }
  294. label { 
  295. display: block;
  296. }
  297. label { 
  298. float: left; 
  299. width: 10em; 
  300. margin-right: 1em;
  301. }
  302. fieldset li { 
  303. float: left; 
  304. clear: left; 
  305. width: 100%; 
  306. padding-bottom: 1em;
  307. }
  308. fieldset { 
  309. float: left; 
  310. clear: left; 
  311. width: 100%; 
  312. margin: 0 0 1.5em 0
  313. padding: 0;
  314. }
  315.  
  316. fieldset ol { 
  317. padding-top: 0.25em;
  318. }
  319. fieldset { 
  320. float: left; 
  321. clear: both; 
  322. width: 100%; 
  323. margin: 0 0 1.5em 0
  324. padding: 0
  325. border: 1px solid #BFBAB0; 
  326. background-color: #F2EFE9;
  327. }
  328.  
  329. </style>
  330. </head>
  331. <body>
  332. <h1>Generate a CodeIgniter "Form View" from a CSV input</h1>
  333. <h2>Just an experiment in code generation</h2>
  334. <h3>Format of input</h3>
  335. <p>
  336. Every line is a form field. The "columns" from left to right are:
  337. </p>
  338. <ol>
  339. <li>Field Name</li>
  340. <li>Field Type (text, radio, checkbox, select, textarea)</li>
  341. <li>Field Label</li>
  342. <li>CodeIgniter rules</li>