You are viewing revision #7 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version or see the changes made in this revision.
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,
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...
Nice
Absolute newbies can learn something from this post. Simple recipes like this shall keep coming to encourage more developers to start working on this amazing framework.
@raheelk2k
yes i also just think so...
i mark that newbie find some difficulties on startup so currently my focus is to help newbie as currently much growth in new users.
Useless tutorial
This is design pattern fail, because you need to use 2 joins of same table to get data of company address and home address, database over kill and search performance issues.
If you use this database design pattern you use 3 tables,
User Table
id
name
surname
email
User To Address Table
user_id
address_id
Address Table
id
street
city
state
@iivano71
yes my friend, you might be right as you watch this article as database normalization point of view and as you compare with search performance.
but currently for demo purpose i am not using that much normalization, and i created demo app just for tutorials...
and it will be definitely useful to users wheather you give plus point or minus ;)
and as you say (Useless tutorial), one can not underestimate this just for tightly normalization issue.
cheers..
I don`t see the good point ?
Every good pattern is good to share, but teaching programmer to do something wrong is bad because you need to write something twice. If you lose time you lose money.
@iivano71
you don't see the good point? ( because you haven't see the good point, you just focused on tightly normalized pattern )
how you will be achive this functionality with tightly normalized pattern?
this fulfill all functionalities as i created database as i don't use that much tightly normalized pattern, what will from your side?
Validation errors?
Problem here is not the database pattern as much as the code you suggested above.
I understand you want to provide a very simple code for new users to understand, but if they try to use the above code they will get more problems than reading a more complex example.
Check a bit your code and see what happens if any of the models gets a validation error?
"Collecting Tabular Input"
The official tutorial for Yii, called "The Definitive Guide to Yii", has a chapter on this subject: "Working with forms: Collecting Tabular Input".
The method used in the official tutorial can attach an unlimited number of objects. It is far more compact than the method presented here. For now, I would not recommend to follow this wiki page. Yet it would be a good idea to rewrite it so that it extends the tutorial, with more details and a link towards the chapter, but this rewrite would be hard work and would need a better comprehension of Yii's mecanisms.
BTW, there's a big bug in the controller shown above: no validation is performed. More exactly, the data will be partially saved, with no error message, when an address is not valid. The worse part is that it will crash when you try to update it (
findByPk()
will returnfalse
, though it will be used as an object).@mdomba , @Francois.Gannaz
@mdomba , @Francois.Gannaz thankx for comment,
The validation code is also working fine, as the controller get post data it will check validation on xxxModel->save method(validation onclick submit button).
and all validation is working fine as i am providing two screenshot below of create and update page,
@kiran123
Please do not post very big images as it's difficult to read the wiki and especially comments... I manually edited your comment to set a fixed width.
As for the validation maybe it works for you but then you are using different or additional code as I really don't see how it could work as expected from your code above.
For example: following your code in Create Controller... you are first calling $addressModel_1->save(); so this address will be saved even if there will be validation errors for the second address or user.
Another example: you have the line if($model->save()) redirect... so even if there are validation errors for any or both of the addresses the user would be saved and the user redirected.
All this of course if the validation is done on the server side as the enableAjaxValidation is set to false above... if that would be set to true... than the validation would occur on every field input.
@mdomba
yes that was the problem as user data could save without address data(but in my model home_address_id, company_address_id was required so it will not save data without that fields)...
Anyway the controller code is updated and made all three models validate before save, and article image made smaller.
will take care for this type of mistakes next time :)
thankx for pointing to that.
@kiran123
Even with those fields as required, as I wrote before it could happen that one address gets saved when the other has a problem... so you would end with orphan addresses in that table..
Now the code is much much better...
This example is very good for simple needs, for example when there should always be only 2 addresses.
Thanks for the contribution.
Just one note for those interested... to be really, really sure that all the data will be saved in different tables... transactions should be used.
would use relations more.
would simplify code using relations more.
$model=new User; // User Model $addressModel_1 = new Address; // Address Model $addressModel_2 = new Address; // Address Model if(!empty($_POST)){ // snip // if validation fails display unsaved addresses $model->home_address = $addressModel_1; $model->company_address = $addressModel_2; } this->render('create',array( 'model'=>$model, ));
@Imre
@Imre,
don't understand what you want to explain,
and your suggest code can't fulfill above requirement..
Great!
great, thanks your wiki... i like it
use loop for multiple instances
Thanks for nice article.
Can you please share a code for multiple instances with loop.Since, i have some confusion about this part
// Validate all three model $valid=$addressModel_1->validate(); $valid=$addressModel_2->validate() && $valid; $valid=$model->validate() && $valid;
Here are some issue with the validation message for multiple instance for same model in loop.While validating multiple instance i don't get validation message for all model instance in this case.
Thanks in Advance.
@Kiran
Hi thanks for article
i have to show form with home address fields and then a checkbox for mailing address 'same as above'. user fill home address and then check the checkbox as 'same as above' or user uncheck the checkbox then an another address fileds same as above in same form appears.when both address fields shows it works fine but when user ticks 'same as above'.it gives validation for hidden address fields.
i had solved this problem by putting conditional validation in controller but how can i solve it for client site validation?
@prasanth
You can put jQuery code as on tick checkbox it will copy all first address field into second (disabled) address field.
Then on submit it will not give validation err.
Error
I got this error when i run it..
Fatal error: Call to a member function isAttributeRequired() on a non-object in C:\xampp\htdocs\yii\framework\web\helpers\CHtml.php on line 1414
@vijay
You did not pass variables to _form.php
Modify renderPartial code in create.php and update.php to
$this->renderPartial('_form', array( 'model'=>$model, 'childModel_1'=>$childModel_1, 'childModel_2'=>$childModel_2, ));
Same things with an array ?
Hi,
Is this possible with an array of adresses rather than address1 and address2 ?
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.