Hacking the Form Helper and Editing Multiple Rows with CakePHP 1.1

CakePHP 1.2 is a fantastic improvement over 1.1. However like many, my work has a stable release only policy. So I’m stuck using 1.1 at work for the time being. But after drinking the 1.2 juice, 1.1 is missing some of the potent automagic flavours. First up editing multiple rows in models. The stock syntax is lacking in that it doesn’t support this at all. So I did some searching, primarily through the google groups and Mozilla’s Addon Site Repository What I found is that you can make multiple row editing possible in 1.1 it just requires some wierd syntax and some hacking.

Multiple Row Syntax

Show Plain Text
  1. $form->generateInputDiv('Model/0][fieldname', 'Fieldname');

The above will make a input name of data[Model][0][fieldname] which is exactly what we want. This will create the proper array structures in the POST data. One minor problem is that neither the HtmlHelper nor the FormHelper quite understand how to get vaildation information or field values for these structures.

Hacking Time!

Hacking the core of any framework is a bad idea. As if you ever upgrade the framework your changes will vanish. So a good work around that Cake supports for the Helpers is overriding. You can override a core helper by placing a copy in your app/views/helpers directory. This leaves the core helpers alone, and allows you to add all the features you need. A comprimise, but a maintainable one. So to deal with the POST arrays that come from the above syntax I will usually add the following in to my HtmlHelper.

First we need to change how HtmlHelper::tagValue works.

Show Plain Text
  1. /**
  2.  * Returns value of $fieldName. False if the tag does not exist.
  3.  *
  4.  * @param string $fieldName Fieldname as 'Modelname/fieldname' string
  5.  * @return string htmlspecialchars Value of the named tag.
  6.  * @access public
  7.  */
  8.     function tagValue($fieldName, $escape = false) {
  9.         $this->setFormTag($fieldName);
  10.         //Add in support for multiple model syntax 'Model/index][field'
  11.         if (strpos($this->field, '][')) {
  12.             preg_match('/^[0-9]+/', $this->field, $index);
  13.             $index = $index[0];
  14.             $field = substr($this->field, strpos($this->field, '][') + 2);
  15.             if (isset($this->params['data'][$this->model][$index][$field])) {
  16.                 return ife($escape, h($this->params['data'][$this->model][$index][$field]), $this->params['data'][$this->model][$index][$field]);
  17.             } elseif (isset($this->data[$this->model][$index][$field])) {
  18.                 return ife($escape, h($this->data[$this->model][$index][$field]), $this->data[$this->model][$index][$field]);
  19.             }
  20.         }
  21.         if (isset($this->params['data'][$this->model][$this->field])) {
  22.             return ife($escape, h($this->params['data'][$this->model][$this->field]), $this->params['data'][$this->model][$this->field]);
  23.         } elseif (isset($this->data[$this->model][$this->field])) {
  24.             return ife($escape, h($this->data[$this->model][$this->field]), $this->data[$this->model][$this->field]);
  25.         }
  26.         return false;
  27.     }

The above will now work with both the old and normal syntax as well as allow us to access multi-row models using the above syntax. I’ve found that changing tagValue avoids having to change every other method in the HtmlHelper. Generally I’m using Advanced Model Validation I add in error message retrieval to my FormHelper extending what is in the advanced model validation article to be compatible with multi row forms.

Error Messages in FormHelper

Show Plain Text
  1. /**
  2. * Error Message getter
  3. *
  4. *
  5. * @param string $fieldName
  6. * @return mixed bool on no error, error string on field error
  7. * @access public
  8. *
  9. */ 
  10.     public function errorMessage($target) {
  11.         list($model, $field) = explode('/', $target);
  12.         $model = ucwords(strtolower($model)); //some reason validationErrors gets lowercased??
  13.         //check for multiple sub models
  14.         if (strpos($field, '][')) {
  15.             preg_match('/^[0-9]+/', $field, $index);
  16.             $index = $index[0];
  17.             $field = substr($field, strpos($field, '][') +2);
  18.             if (is_numeric($index)) {          
  19.                 if (isset($this->validationErrors[$model][$index][$field])) {
  20.                     return '<p class="error-message">'.$this->validationErrors[$model][$index][$field].'</p>';
  21.                 }
  22.             }
  23.         }        
  24.         if (isset($this->validationErrors[$model][$field])) {
  25.               return '<p class="error-message">'.$this->validationErrors[$model][$field].'</p>';
  26.         } else {
  27.             return false;
  28.         }
  29.      }

Now we can get the Validation Errors made by the Advanced Model Validation As well as get errors for multi row forms. Some simple but very handy upgrades to the FormHelper and HtmlHelper. I have a few more useful hacks and will be posting those as well. So check back.

Comments

Do you have to copy the whole helper to over ride just one function?

Is it possible to extend the helper?

Iain on 11/3/10

how to post multiple form values in single field of database in cake php

kriti on 10/11/11

hi mark,
I need your help, on a project am working on. I have a member and a member can have many telephone numbers.
Eg
Member table
id
name

telephone table
id
member_id
cell

i want to create a form so that when i add a member i can also add as many telephone numbers.
My solution was to redirect to telephone/add controller after member/add is saved and but the when a telephone is saved it will redirect to telephone/add but this is a link to proceed with the registration, if the user has entered all the numbers.
Is this solution OK or you can suggest a better one or an article to read to get better insight to my issue
Thanks

walter on 3/7/14

Comments are not open at this time.