How to use Multiple instances of the same model in the same form

When i had created this functionality then i found some difficulties and not got much idea from wiki and forums. so, i think this will be useful for newbie users and save time of other developers when create related functionality. I refer Collecting Tabular Input tutorial but not got clear idea for create/update.

Here in my demo i had two tables as, User(table) can have multiple address ( home, company ) which comes from Address(table)

Model generated by gii and no any change in model.

User Table
---id
---name
---surname
---email
---home_address_id ( data refer to Address table )
---company_address_id ( data refer to Address table )
Address Table
---id
---street
---city
---state

Developer can also use loop for multiple instances here in my demo i explained with two.

Create Controller
public function actionCreate()
{
	$model=new User;  // User Model
	$addressModel_1 = new Address;  // Address Model
	$addressModel_2 = new Address;  // Address Model
		
	if(!empty($_POST))
	{
		// Set attribute for home address
		$addressModel_1->attributes=$_POST['Address'][1];
		// Set attribute for company address
		$addressModel_2->attributes=$_POST['Address'][2];
		// Set attribute for user data
		$model->attributes=$_POST['User'];  

		// Validate all three model
		$valid=$addressModel_1->validate();
		$valid=$addressModel_2->validate() && $valid;
		$valid=$model->validate() && $valid;
			
		if($valid)
		{ 
			$addressModel_1->save();
			$homeAddressId = $addressModel_1->id;
							
			$addressModel_2->save();
			$companyAddressId = $addressModel_2->id;

			// Set saved address as user home id
			$model->home_address_id = $homeAddressId;
			// Set saved address as user company id
			$model->company_address_id = $companyAddressId;  
			$model->save();
			$this->redirect(array('view','id'=>$model->id));
		}
	}
	$this->render('create',array(
		'model'=>$model,
		'addressModel_1'=>$addressModel_1,
		'addressModel_2'=>$addressModel_2,
	));
}
Update Controller
public function actionUpdate($id)
{
	// Load user data in model
	$model=User::model()->findByPk($id);
	// Load address data in addressModel_1
	$addressModel_1=Address::model()->findByPk($model->home_address_id);  
	// Load address data in addressModel_2
	$addressModel_2=Address::model()->findByPk($model->company_address_id);  

	if(!empty($_POST))
	{
		// Set attribute for home address
		$addressModel_1->attributes=$_POST['Address'][1];
		// Set attribute for company address
		$addressModel_2->attributes=$_POST['Address'][2];
		// Set attribute for user data
		$model->attributes=$_POST['User'];

		// Validate all three model
		$valid=$addressModel_1->validate(); 
		$valid=$addressModel_2->validate() && $valid;
		$valid=$model->validate() && $valid;
			
		if($valid)
		{ 		
			$addressModel_1->save();
			$addressModel_2->save();
			$model->save();
		}
	}
	$this->render('update',array(
		'model'=>$model,
		'addressModel_1'=>$addressModel_1,
		'addressModel_2'=>$addressModel_2,
	));
}
View Form

For create and update same page render as _form, from below view code you can get the idea on how to pass models and set attribute names for elements..

<div class="form">
<?php $form=$this->beginWidget('CActiveForm', array(
	'id'=>'user-form',
	'enableAjaxValidation'=>false,
)); ?>

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

	<div class="row">
		<?php echo $form->labelEx($model,'name'); ?>
		<?php echo $form->textField($model,'name',array('size'=>50,'maxlength'=>50)); ?>
		<?php echo $form->error($model,'name'); ?>
	</div>
	<div class="row">
		<?php echo $form->labelEx($model,'surname'); ?>
		<?php echo $form->textField($model,'surname',array('size'=>50,'maxlength'=>50)); ?>
		<?php echo $form->error($model,'surname'); ?>
	</div>
	<div class="row">
		<?php echo $form->labelEx($model,'email'); ?>
		<?php echo $form->textField($model,'email',array('size'=>50,'maxlength'=>50)); ?>
		<?php echo $form->error($model,'email'); ?>
	</div>
	
	//////////////////////////////////////////////////////
	<h2>Home Address Below</h2>	

	<div class="row">
		<?php echo $form->labelEx($addressModel_1,'[1]street'); ?>
		<?php echo $form->textField($addressModel_1,'[1]street'); ?>
		<?php echo $form->error($addressModel_1,'[1]street'); ?>
	</div>
	<div class="row">
		<?php echo $form->labelEx($addressModel_1,'[1]city'); ?>
		<?php echo $form->textField($addressModel_1,'[1]city'); ?>
		<?php echo $form->error($addressModel_1,'[1]city'); ?>
	</div>	
	<div class="row">
		<?php echo $form->labelEx($addressModel_1,'[1]state'); ?>
		<?php echo $form->textField($addressModel_1,'[1]state'); ?>
		<?php echo $form->error($addressModel_1,'[1]state'); ?>
	</div>
	
	//////////////////////////////////////////////////////
	<h2>Company Address Below</h2>
	
	<div class="row">
		<?php echo $form->labelEx($addressModel_2,'[2]street'); ?>
		<?php echo $form->textField($addressModel_2,'[2]street'); ?>
		<?php echo $form->error($addressModel_2,'[2]street'); ?>
	</div>
	<div class="row">
		<?php echo $form->labelEx($addressModel_2,'[2]city'); ?>
		<?php echo $form->textField($addressModel_2,'[2]city'); ?>
		<?php echo $form->error($addressModel_2,'[2]city'); ?>
	</div>	
	<div class="row">
		<?php echo $form->labelEx($addressModel_2,'[2]state'); ?>
		<?php echo $form->textField($addressModel_2,'[2]state'); ?>
		<?php echo $form->error($addressModel_2,'[2]state'); ?>
	</div>

	<div class="row buttons">
		<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>
	</div>

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

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

The demo looks like,

All field validation

For every single entry in User table there will be two entry in Address table (home and company).

I hope this will help to many...

10 3
23 followers
Viewed: 93 341 times
Version: 1.1
Category: How-tos
Written by: kiran sharma
Last updated by: Maurizio Domba Cerin
Created on: Aug 1, 2012
Last updated: 12 years ago
Update Article

Revisions

View all history