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.