My favorites | Sign in
Project Logo
                
Search
for
Updated Mar 21, 2008 by maartenvanvliet
Labels: Featured
ModelToForm  
Explanation as to how you can create forms from models

Introduction

It's simple, you declare your model with all validation rules that come with it. Nothing new here, but now you declare a form which retrieves the model and you select which fields the form should have. You can now ask the form to display with all the values from the model.

Example of declaring a form.

class User_Form_Core extends Model_Formation{
	
//Exclude these fields, all other fields in the model are displayed
protected $exclude=array('id','modified','created','role_id');

//Include these fields, all fields not in here are not displayed
protected $form_fields=array();

//Which model this form takes after
protected $_model='User_Model';

protected $disabled=array('modified'); //shows the form field but disabled so it can't be edited.

}

Example of the model

class User_Model extends ORM{
	protected $validate=array(
	'name'=>array(	
		'rules'=>array(array('Rule_Max_Length',50)),
		'pre_filters'=>array('ucfirst')
		
		),
	'email'=>array(
		'type'=>'email',
		'rules'=>array('Rule_Required'),
		),
	'preference'=>array(
		'type'=>'checklist',
		),
	'comment'=>	array(
		'pre_filters'=>array('trim'))
	);
}

Now you can start generating your forms: A random controller

public function create_user()
{
    $form=new User_Form();
    if($form->save())
    {
       echo 'User saved';
    }
    else
    {
       echo $form->render();
    }

}
public function edit_user($id)
{
    $form=new User_Form($id);//or $form=new User_Form(new User_Model($id));
    if($form->save())
    {
       echo 'User saved';
    }
    else
    {
       echo $form->render();
    }
}

That's it, the render() method will give you a form with rules, validation and all retrieved from the model. Easy for your backend and perhaps your frontend if you need some forms quickly.

Remember, the form is an instance of Formation so you can do this:

public function edit_user($id)
{
    $form=new User_Form($id);
    $form->add_element('password','password_match')->add_rule('Rule_Matches',$form['password']);
    if($form->save())
    {
       echo 'User saved';
    }
    else
    {
       echo $form->render();
    }
}

You can change the entire form after you retrieved it from the model.

But you can also change stuff from your User_Form class, such as the template

class User_Form_Core extends Model_Formation{
	
//...
protected $template='your_template';

}

Comment by mkjems, Mar 15, 2008

Should the model not extend ORM ?

Comment by mkjems, Mar 15, 2008

I'm having some trouble getting this to work.

I have a Page controller:

  public function fresh(){
    $form=new Page_Form(new Page_Model());
    if($form->save())
    {
      $this->template->main= 'Page saved';
    }
    else
    {
      $this->template->main= $form->render();
    }
  }

and a Page_Form?:

class Page_Form_Core extends Model_Formation{

protected $exclude = array();

protected $form_fields=array('title','menu', 'id', 'order');

protected $_model='Page_Model';

}

And a model:

class Page_Model extends ORM
{
  protected $validate=array();                                                           
  //protected $custom_field_data=array('title'=>'title');

And this is my table

TABLE `pages` (
  `id` mediumint(9) NOT NULL auto_increment,
  `title` varchar(255) NOT NULL,
  `menu` tinyint(1) NOT NULL default '0',
  `order` mediumint(9) NOT NULL,
  PRIMARY KEY  (`id`)
)

But the build_form() method does not find anything

protected function build_form($guess_fields=true)
{
   //pr($this->_model->field_data());		
   foreach($this->_model->field_data() as $name=>$property) 
   {		
      if(in_array($name,$this->exclude))
	continue;
      if($this->form_fields!=array() and !in_array($name,$this->form_fields))
	continue;
	......			

It looks like the function $this->model->field_data() is meant to get the fields in the table but the array is always empty.

$this->model looks like this

Page_Model Object
(
    [has_many:protected] => Array
        (
            [0] => columns
        )

    [validate:protected] => Array
        (
        )

    [auto_save:protected] => 
    [class:protected] => page
    [table:protected] => pages
    [select:protected] => 
    [where:protected] => 
    [object:protected] => stdClass Object
        (
            [id] => 
            [title] => 
            [menu] => 
            [order] => 
        )

    [changed:protected] => Array
        (
        )

    [has_one:protected] => Array
        (
        )

    [belongs_to:protected] => Array
        (
        )

    [belongs_to_many:protected] => Array
        (
        )

    [has_and_belongs_to_many:protected] => Array
        (
        )

)

Any suggestions ?

Comment by mkjems, Mar 15, 2008

I think I understand something.. field_data() is a method of The Database object. You can say $db = new Database(); $db->field_data($tablename); and It will return an array containing each field in the table as the array key and an array of field metadata as the value. Different databases will return different sets of information about the fields.

But I don't think ORM models have this method. In fact i think they explicitly deny this

ORM.php line 430:

// Do not allow query methods
if (preg_match('/query|get|list_fields|field_data/', $method))
  return $this;
Comment by mkjems, Mar 15, 2008

Ok , here is what I did to make it work for me:

protected function build_form($guess_fields=true)
    {
    // Martin hack..first find table name from $_model  
    $table= str_replace('_Model','',get_class($this->_model));
    $table = inflector::plural(strtolower($table));
    // get field data
    $db = new Database(); 
    $fieldData = $db->field_data($table);
    // transform to array
    foreach($fieldData as $d){
      $field = get_object_vars($d);
      // lowercase keys
      foreach($field as $name=>$value){
        $field_two[strtolower($name)]=$value;
      }      
      // build fields array
      $fields[$field_two['field']]= $field_two;
    }
    // end Martin hack
    
    foreach($fields as $name=>$property)
    {
	if(in_array($name,$this->exclude))
	     continue;
        ....
Comment by mkjems, Mar 15, 2008

Can't help thinking we must be doing things in a different way since you get the field data by doing : $this->model->field_data() and I have to make the above hack... ???

Comment by maartenvanvliet, Mar 17, 2008

I corrected the User_Model? extends ORM, thx

You're right in saying I did it a little different. ORM always retrieves database field data and puts it in a static property, using a new method I retrieve this information and utilize it for looping over the fields and add the length rules.

Comment by write2howie, Jul 06, 2008

This library is great. Thank you. Onr small problem. Can't seem to set a required validation rule if field type is set to dropdown, or checklist. Always fails validation

Comment by write2howie, Jul 06, 2008

Hmm.. another problem. Formation screws up my ORM many-to-many table in another controller. Insists on writing SQL using roles_users rather than users_roles. fine once I remove the module.

Comment by maartenvanvliet, Jul 06, 2008

Classis case of bug vs. feature. There is a method in MY_ORM file named find_related or something. This is actually a patch that will enable from User_Model? find_related_roles as well as Role_Model?->find_related_user. The order of the pivot table has to be alphabetic though(so roles_users like in RoR) Kohana only supports find_related_x one-way only.Remove the find_related method and it's fixed.

Comment by migajek, Dec 16, 2008

hi,is there a version working in kohana 2.2?

Comment by anzelmy, Dec 29, 2008

Hello, I have a problem, how to update record? Even if I add ID field the Formation is trying to create a new record (it uses INSERT INTO not UPDATE sql statement. How to solve it?


Sign in to add a comment
Hosted by Google Code