Dynamic Tabular Input

Hi I’ve been following this collecting tabular input wiki. I chose not to use the multimodelform extension.

I have a multi model form w/ tabular input. When I save the Lease model, a new record is saved for each attribute. Can someone advise?

PropertyController




public function actionCreate()

	{				       

       $property = new Property;

       $leases = array(new Lease);

              

       //If everything is set, check and process the inputs

       if(isset($_POST['Property']))

       {

         $property->attributes = $_POST['Property'];

         $valid = $property->validate();

         

         

       	//If everything is set, check and process the inputs

       	if(isset($_POST['Lease']))

       	{

	        $valid = true;


        	 //Add to the Lease array a new Lease for every instance in $_POST['Lease']

         	foreach ($_POST['Lease'] as $id=>$lease)

         	{

            $leases[$id] = new Lease;

           	$leases[$id]->attributes = $lease;

            $valid = $leases[$id]->validate() && $valid;  	

         	}

                   

        	if($valid && $property->save(false))

        	{        

        		foreach ($_POST['Lease'] as $id=>$lease)

        		{

        			$leases[$id]->property_id = $property->id;        			

			        $leases[$id]->save(false);

        		}

        	}

        

        $this->redirect(array('view','id'=>$property->id));  

        }

      }

       

     $this->render('create', array(

       'property'=>$property,

       'leases'=>$leases

    ));

  } 



property/_form




<table id="dynoTable">

  <thead>

    <tr><th /><th>Tenant</th><th>Type</th><th>Sq Ft</th><th>Lxp</th><th>Annual Rent</th><th /><th /></tr>

  </thead>

  <?php foreach($leases as $id=>$lease): ?>

    <tr>

        <td><img class="icon-move"/></td>

        <td><?php echo CHtml::activeTextField($lease,"[$lease->id]tenant", array('class'=>'input-medium')); ?></td>

        <td><?php echo CHtml::activeTextField($lease,"[$lease->id]lease_type", array('class'=>'input-small')); ?></td>

        <td><?php echo CHtml::activeTextField($lease,"[$lease->id]sf", array('class'=>'input-small')); ?></td>

        <td><?php echo CHtml::activeTextField($lease,"[$lease->id]lxp", array('class'=>'input-small')); ?></td>

        <td><?php echo CHtml::activeTextField($lease,"[$lease->id]rent_annual", array('class'=>'input-small')); ?></td>

        <td><img class="icon-plus-sign" /></td>

        <td><img class="icon-trash" /></td>

    </tr>

    <tr id="add-template">

        <td><img class="icon-move" /></td>

        <td><?php echo CHtml::activeTextField($lease,"[$lease->id]tenant", array('class'=>'input-medium')); ?></td>

        <td><?php echo CHtml::activeTextField($lease,"[$lease->id]lease_type", array('class'=>'input-small')); ?></td>        

        <td><?php echo CHtml::activeTextField($lease,"[$lease->id]sf", array('class'=>'input-small')); ?></td>

        <td><?php echo CHtml::activeTextField($lease,"[$lease->id]lxp", array('class'=>'input-small')); ?></td>

        <td><?php echo CHtml::activeTextField($lease,"[$lease->id]rent_annual", array('class'=>'input-small')); ?></td>

        <td><img class="icon-plus-sign" /></td>

        <td><img class="icon-trash" /></td>

    </tr>

  <?php endforeach; ?>                

</table>

	<button class="btn" id="add-row">Add Tenant</button>  



Dear Friend

I have not used dynotable previously. Because of the difficulty I encountered ,

I reverted back to my own methods.

AIM:

1.Create a parent model along with batch creation of child models.

2.Update parent model and along with batch update of child models.

3.Batch Create/update child models.

Short Summary:

1.In parent form after displaying its attributes,we are going to display child models before submit button.

2.Those child models may be existing in database during update or models failed in validation during create.

3.We are going to register a script to create a child form from a template(hidden) and insert before

submit button.We can attach any number of child forms and also we can detach any child form.

4.In controller, during creation, we are going validate both the parent and all the child models.If validation

fails at any point, we are going back to the form with error display for re submission.

If validation succeeds,we are going to save all the child models with parent model.

5.During update, we are displaying already existiong models in database.Now we can add any number of child forms

in addition.After submission,in controller we have to differentiate the existing child models from new ones.

For simplicty I created two models.

1.District:id,name

2.Block:id,name,population,area,d_id.

One district has many blocks through d_id.

In model District, I have attached a virtual property block.This is purely for displaying

errors of instances of Block as errors of this block property and it can be displayed in

error summary of District form field.

District.php




class District extends CActiveRecord

{   

 public $block;

 public function relations()

	{		

		return array(

			'blocks' => array(self::HAS_MANY, 'Block', 'd_id'),

		);

	}

}



In Model Block, I have declared a function to fetch first error of all the attributes.

Block.php




class Block extends CActiveRecord

{

.....................................

public function fetchErrors()

   {

    $str="|";

    foreach($this->errors as $attribute=>$errors)

            $str=$str.$errors[0]."|";

    return $str;

    }



In _form.php after displaying fields for district name, we are going to add a button(Add Block).

By clicking that button the script is going to generate a block form from the template and it is

going to attach before the submitt button.

The block form is stripped of form tags.Each block form would contain remove button.The form will

disappear once we click on remove button.

The whole idea is to render individual attribute names in the following format.

Model[itemNumber][attribute]

In our case, for example Block[1][population] or Block[9][area]

_form.php




<div class="form">


<?php $form=$this->beginWidget('CActiveForm', array(

	'id'=>'district-form',

	'enableAjaxValidation'=>false,

)); ?>


	<p class="note">Fields with <span class="required">*</span> are required.</p>


	<?php echo $form->errorSummary($district); ?>


	<div class="row">

		<?php echo $form->labelEx($district,'name'); ?>

		<?php echo $form->textField($district,'name',array('size'=>60,'maxlength'=>128)); ?>

		<?php echo $form->error($district,'name'); ?>

	</div>


<!--Remove Button---------->

    <div class="row buttons">

		<?php echo CHtml::button('Add Block',array('id'=>'add')); ?>

	</div>


<!--for each loop to render existing models or those in whom validation failed---------->	

	<?php if(count($blocks)>0) {

	       foreach($blocks as $i=>$block) {?>

	       

	

	

	<div style="border:1px solid gold;padding:10px;margin-bottom:5px;width:330px;float:left;">

		

     <h4 id="head"><?php echo "Block ".$i;?> </h4>

    <button id="remove" style="float:right;" >Remove</button>

    

	<div class="row">

		<?php echo 'Name Of Block'."</br>"; ?>

		<?php echo CHtml::textField('Block['.$i.'][name]',$block->name); ?>

		<?php echo CHtml::error($block,'name'); ?>

	</div>


	<div class="row">

		<?php echo 'Population'."</br>"; ?>

		<?php echo CHtml::textField('Block['.$i.'][population]',$block->population); ?>

		<?php echo CHtml::error($block,'population'); ?>

	</div>


	<div class="row">

		<?php echo 'Area'."</br>"; ?>

		<?php echo CHtml::textField('Block['.$i.'][area]',$block->area);?>

		<?php echo CHtml::error($block,'area'); ?>

	</div>

   

</div>   

			   

<?   }}?>


	<div class="row buttons">

		<?php echo CHtml::submitButton($district->isNewRecord ? 'Create' : 'Save',array('id'=>'butt')); ?>

	</div>


<?php $this->endWidget(); ?>


</div><!-- form -->


<!-- template -->

<div id="block" style="display:none;border:1px solid gold;padding:10px;margin-bottom:5px;width:330px;float:left;">

     <h4 id="head"></h4>

     <button id="remove" style="float:right;" >Remove</button>

	<div class="row">

		<?php echo 'Name Of Block'."</br>"; ?>

		<?php echo CHtml::textField('name'); ?>

	</div>


	<div class="row">

		<?php echo 'Population'."</br>"; ?>

		<?php echo CHtml::textField('population'); ?>

	</div>


	<div class="row">

		<?php echo 'Area'."</br>"; ?>

		<?php echo CHtml::textField('area'); ?>

	</div>

	

    

</div>

<!--end of template -->


<?php 

$blocks=Block::model()->findAll();

Yii::app()->clientScript->registerScript('multi','

var bid='.max(array_keys(CHtml::listData($blocks,'id','name'))).';

$("#add").on("click",function(){

++bid;

var copy=$("#block").clone(true).insertBefore("#butt").attr("id",null).show();


copy.find("#head").html("Block "+ bid);

$.each(copy.find("input"),function(i,j){

$(j).attr("name","Block"+"["+bid+"]"+"["+$(j).attr("name")+"]");

});

});

$("body").on("click","#remove",function(){$(this).parent().detach()});

');

?>



DistrictController.php




public function actionCreate()

{

	$district=new District;

	$blocks=array();

        $error=false;


	if(isset($_POST['District']))

	{

		$district->attributes=$_POST['District'];

		$district->validate();

        }

		

	if(isset($_POST['Block']))

	{   

		foreach($_POST['Block'] as $i=>$j)

                {

		    $block=new Block;

		    $block->attributes=$j;

		    $blocks[$i]=$block;

		    if(!$block->validate(array('name','population','area')))

                    {

			   $district->addError('block',$block->fetchErrors(). "on block $i");

			   $error=true;

                    }

		}


		   if(!$error && count($blocks)>0)

                   {

		        $district->save();

		        foreach($blocks as $block)

		        { 

                          $block->d_id=$district->id;

		          $block->save();

		        }	


		    $this->redirect(array('district/index'));

		 }   

		

	}

        

       		$this->render('create',array(

			'district'=>$district,

			'blocks'=>$blocks,

			

		));

	}



create.php




<?php

$this->breadcrumbs=array(

	'Districts'=>array('index'),

	'Create',

);


$this->menu=array(

	array('label'=>'List District', 'url'=>array('index')),

	array('label'=>'Manage District', 'url'=>array('admin')),

);

?>


<h1>Create District</h1>


<?php echo $this->renderPartial('_form', array('district'=>$district,'blocks'=>$blocks)); ?>



DistrictController.php




public function actionUpdate($id)

{

	$district=$this->loadModel($id);

	$blocks=array();

	$error=false;


	foreach(Block::model()->findAll("d_id=$district->id") as $block)

             $blocks[$block->id]=$block;

		


	if(isset($_POST['District']))

	{

	      $district->attributes=$_POST['District'];						

	}


        if(isset($_POST['Block']))

	{   

	      $blocks=array();

	      foreach($_POST['Block'] as $i=>$j)

	      {

	          if(Block::model()->findByPk($i)!==null)

		  {     $block=Block::model()->findByPk($i);

			$block->attributes=$j;

		        $blocks[$block->id]=$block; 

	          }

		  else

                  {	 

			$block=new Block;

			$block->attributes=$j;

			$blocks[$i]=$block;

		   }

		   if(!$block->validate(array('name','population','area')))

                   {

			 $district->addError('block',$block->fetchErrors(). "on block $i");

			   $error=true;

                   }

	      }

	       if(!$error &&  $district->validate())

              {    

		    $district->save();

		    foreach($blocks as $block)

		    { 		   

			 $block->d_id=$district->id;

		         $block->save();           

		    }	

		    $this->redirect(array('district/index'));

	     }  

	} 


	$this->render('update',array(

			'district'=>$district,

			'blocks'=>$blocks,

		));

}




update.php




<?php

$this->breadcrumbs=array(

	'Districts'=>array('index'),

	$district->name=>array('view','id'=>$district->id),

	'Update',

);


$this->menu=array(

	array('label'=>'List District', 'url'=>array('index')),

	array('label'=>'Create District', 'url'=>array('create')),

	array('label'=>'View District', 'url'=>array('view', 'id'=>$district->id)),

	array('label'=>'Manage District', 'url'=>array('admin')),

);

?>


<h1>Update District <?php echo $district->id; ?></h1>


<?php echo $this->renderPartial('_form', array('district'=>$district,'blocks'=>$blocks)); ?>




As you see code has become very lenthy.

Kindly bear with me.

This is because I have extended the logic to the update also.

Kindly give personal messages, if you find difficulty.

Regards.

Here is the couple of screenshots.

3613

create.png

3615

update.png

Not sure whether your problem was solved or not. But I was able to do dependent scenarios with multi model form extension. If you could not make it work, I may help.