Assume we want to use a single HTML form to collect input for both model A and model B, and we want to display input errors (if any) in the same error summary box. We can define the following action code:
public function actionCreate()
{
$a=new A;
$b=new B;
if(isset($_POST['A'], $_POST['B']))
{
// populate input data to $a and $b
$a->attributes=$_POST['A'];
$b->attributes=$_POST['B'];
// validate BOTH $a and $b
$valid=$a->validate();
$valid=$b->validate() && $valid;
if($valid)
{
// use false parameter to disable validation
$a->save(false);
$b->save(false);
// ...redirect to another page
}
}
$this->render('create', array(
'a'=>$a,
'b'=>$b,
));
}
To add these new fields to your Create form, add your 2nd table fields no stored in a 2nd model.
A's create.php:
<?php echo $this->renderPartial('_form', array('a'=>$a, 'b'=>$b)); ?>
You usually place the specific fileds in the _form.php file if you are working off Gii created CRUD files.
<?php echo CHtml::beginForm(); ?>
<?php echo CHtml::errorSummary(array($a,$b)); ?>
<!-- ...input fields for $a, $b... -->
<div class="row">
<?php echo $form->labelEx($a,'a_field'); ?>
<?php echo $form->textField($a,'a_field'); ?>
<?php echo $form->error($a,'a_field'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($b,'b_field'); ?>
<?php echo $form->textField($b,'b_field'); ?>
<?php echo $form->error($b,'b_field'); ?>
</div>
<?php echo CHtml::endForm(); ?>
The above approach can also be used if we have more than two models to deal with.
If $b has FK from $a's auto_increment PK
if($valid) { if($a->save(false)) { $b->a_id = $a->id; $b->save(false); $this->redirect(array('somewhere')); } }
help..
i already try to follow this wiki, but unfortunately its not work for me.. is there anyone can help me. thanks.. =) this is my code..
KategoriController
public function actionCreate() { $model=new Kategori; $sub=new Sub; if(isset($_POST['Kategori'], $POST['Sub'])) { // populate input data to $a and $b $model->attributes=$_POST['Kategori']; $sub->attributes=$_POST['Sub']; // validate BOTH $a and $b $valid=$model->validate(); $valid=$sub->validate() && $valid; if($valid) { if($model->save(false)) { $sub->kategori_id = $model->id; $sub->save(false); $this->redirect(array('view','id'=>$model->id)); } } } $this->render('create', array( 'model'=>$model, 'sub'=>$sub, )); }
Kategori create.php
<?php echo $this->renderPartial('_form', array('model'=>$model, 'sub'=>$sub)); ?>
kategori _form.php
<div class="form"> <?php $form=$this->beginWidget('CActiveForm', array( 'id'=>'kategori-form', 'enableAjaxValidation'=>true, )); ?> <p class="note">Fields with <span class="required">*</span> are required.</p> <?php echo $form->errorSummary(array($model, $sub)); ?> <div class="row"> <?php echo $form->labelEx($model,'kategori'); ?> <?php echo $form->textField($model,'kategori',array('size'=>45,'maxlength'=>45)); ?> <?php echo $form->error($model,'kategori'); ?> </div> <div class="row"> <?php echo $form->labelEx($sub,'sub'); ?> <?php echo $form->textField($sub,'sub',array('size'=>45,'maxlength'=>45)); ?> <?php echo $form->error($sub,'sub'); ?> </div> <div class="row"> <?php echo $form->labelEx($sub,'kategori_id'); ?> <?php echo $form->textField($sub,'kategori_id'); ?> <?php echo $form->error($sub,'kategori_id'); ?> </div> <div class="row buttons"> <?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?> </div> <?php $this->endWidget(); ?> </div><!-- form -->
a question on a little bit more complex implementation
Please check this question which is directly related with the solution proposed in this wiki:
http://www.yiiframework.com/forum/index.php?/topic/22539-passing-posted-data-from-a-form-in-further-steps/
In case this comment is not accepted please ask me to remove it. I just wanted to link the followers of this wiki to my forum question
If $b has FK from $a's auto_increment PK (additional comment)
jmcmasterj's method works, but not if $b's FK is required (which is of course likely).
The problem is the validation. If you try to validate both $a and $b before saving you'll get an error.
// validate BOTH $a and $b $valid=$a->validate(); $valid=$b->validate() && $valid; // $b won't be valid because the FK hasn't been set yet. if($valid) { if($a->save(false)) { $b->a_id = $a->id; $b->save(false); $this->redirect(array('somewhere')); } }
If the FK is required, you need to change that part of the controller. Here's one way of doing it:
$valid=$a->validate(); if($valid) { if($a->save(false)) { $b->a_id = $a->id; if ($b->save()) // not false because it hasn't been validated { $this->redirect(array('somewhere')); } }
$b FK problem
Hi daniel.odonnell
I have a problem with your second method, because it keeps on creating $a records in the database until $b validates.
Your first method is better because it does not create any records until both $a and $b validate. Of course the required FK is then a problem.
Any new ideas? Maybe transactions?
gerhard@ecolar.co.za: consider using scenarios
gerhard@ecolar.co.za: consider limiting the 'required' rule on $b for specific scenario, but not the 'initial creation scenario'.
For example, consider having user and profile AR/tables. On registration, you need date to be fed into both. Yet, as already noted in the comments before this one, that will be a problem if 'profile' is validated before is has the user_id filled in. Yet, you need the 'user' to be saved before in order to get this id before you even attempt initial validation of 'profile' (just as you noted...).
So, if you limit the 'require' rule on the 'user_id' for specific scenarios but NOT the 'registration' scenario, then you can get along with this (validate both, save 'user', grab its user_id, save 'profile'). this has the downside of needing to put all scenarios in the 'required' rule for 'user_id', and maintaining that.
Another option I just thought of is to note in the user's session (or your favorite persistent per-user storage) the fact that 'user' object has been created already and not attempt to save it each time 'profile' objects failed validation (after creation of 'user' row in the DB).
Another solution
is described here: http://www.yiiframework.com/forum/index.php/topic/30928-validation-perform-if-not-in-specific-scenario/
Tks a lot
You are the very best ... I user this to translate all my website into any language.
4 ways to save $a and $b
I would personally go with transaction.
if ($a->validate()) { $transaction = Yii::app()->db->beginTransaction(); $success = $a->save(false); $b->fk_a = $a->id_a; $success = $success ? $b->save(false) : $success; if (!success) $transaction->commit(); else $transaction->rollBack(); }
Alternately if you can't use transactions you can save $a save $b and if $b fails delete $a (I don't like it though 'cause it abuses one id if you're using AUTO_INCREMENT
As a third option and best if you can't use transactions I would go with scenarios
if ($a->validate()) { $b->scenario = 'errors_check'; if ($b->validate()) { $a->save(false); $b->fk_a = $a->id_a; $b->save(false); } }
Of course errors_check scenario should be valid on all rules except your foreign key rule.
Last but not least you can always create a FormModel. Of course in your case it will duplicate lots of code (bad) but for more complex rules sets with more AR classes used on a form, FormModels are quite cool (FormModels are Models that don't have a physical table in the database ;)
Thank you so much!
Thank you for this wonderful article. I was stuck in this problem itself and because of this I could manage.
Regards,
How bout Bitwise operators?
if($a->validate() & $b->validate());
in this case php won't be using its typical short-circuit
How to do so for two or more action of one controller?
So Thank`s for this article.
according to above, How to use 2 or more action of one controller in just one view file?
one question
But if
A has a ralation with B
And A has a relation with C but this last one has another relation with B
how can i build the action create??
Three models?
Just add C to the code.
the relation is the problem
C isn't problem
the relation A with a record of B
and the relation of C with another B record
In my opinion,
Qiang's tutorial really has nothing to do with Yii (for the most part). It's pure Object-Oriented Programming in PHP. Thanks Qiang & Yang He for your support!
How to save multiple related models in yii [Complete Solution]
http://scriptbaker.com/how-to-save-multiple-related-models-in-yii-complete-solution/
@ Error
when in click create button the page is not loaded it will not be saved.... how can i save those values in the both tables.
Here is my controller..
public function actionCreate() { $model=new Add; $maturityModel_1 = new maturity; // Maturity Model //$addressModel_2 = new Address; // Address Model // Uncomment the following line if AJAX validation is needed // $this->performAjaxValidation($model); if(!empty($_POST)) { $model->attributes=$_POST['Add']; $maturityModel_1->attributes=$_POST['Maturity']; if($valid) { $model->save(false); $maturityModel_1->save(false); $this->redirect(array('admin')); } } $this->render('create',array( 'model'=>$model, 'maturityModel_1'=>$maturityModel_1, )); }
RE: #17691
Hi,
check your code.
Also post the forum thread here. :)
RE: #17691
May be relational rules issues. show your full code with relational array from the models.
Error Fix.... Now Update
@KonApaz @Rohit Suthar
Thank you for ur response. I done the create() using this article.. Before i did something went wrong but now i clearly finished..
Now i stuck with the problem, that is how can i Work with the update().
Am using the code:
public function actionUpdate($id) { $model=$this->loadModel($id); if(isset($_POST['Add'])&&isset($_POST['Maturity'])) { $model->attributes=$_POST['Add']; $b = Maturity::model()->findByAttributes(array('code'=>$model->code)); if ($b == null) { $b = new maturity; $b->code = $model->code; } $b->attributes=$_POST['Maturity']; if ($model->save()) { $b->save(); } $this->render('update',array( 'model'=>$model, )); } }
When i click update button it will not return any id or something am new to this concept help me out....
RE #17708
What did you mean "not return id" ?
which model ? $model or $b ?
What do you try to do ? copy data from one model to another one?
May be you have to create a post to forum to get a complete answer, giving more details.
@RE
I Have 2 tables 1.add 2.maturity. In Addcreate form having 9 fields, on that 3 fileds are need to save in the maturity table, and rest of them in the add table.
This is the concept.
SO now I finish the create() in my Addcontroller. now the info will be save in the corresponding tables. Now i need to update. already am post my update() from Addcontroller its not working.
which model ? $model or $b ?
$model belongs to Add table and $b belongs to Maturity table.
my create() from addcontroller.
public function actionCreate() { $model=new Add; $b=new Maturity; // Uncomment the following line if AJAX validation is needed // $this->performAjaxValidation($model); //if(isset($_POST['Add'])) if(isset($_POST['Add'], $_POST['Maturity'])) { $model->attributes=$_POST['Add']; $b->attributes=$_POST['Maturity']; $valid=$model->validate(); $valid=$b->validate() && $valid; if($valid) { // use false parameter to disable validation $model->save(); $b->save(); $this->redirect(array('admin')); // ...redirect to another page } //if($model->save()) // $this->redirect(array('view','id'=>$model->add_id)); } $this->render('create',array( 'model'=>$model, 'b'=>$b, )); }
I think I gave u all details I need to finish it soon help me out.. anything needed about this issue am ready to do.
RE: #17708
@Vijay Please update your render like this -
$this->render('update',array( 'model'=>$model, 'b'=>$b, ));
try to check errors using -
$model->getErrors() and $b->getErrors()
or you need to dump it using -
CVarDumper::dump($model->getErrors(),10,true);
RE: #17710
@Vijay
Try this update function code -
public function actionUpdate(){ $model=$this->loadModel($id); $b = Maturity::model()->findByAttributes(array('code'=>$model->code)); if(isset($_POST['Add'], $_POST['Maturity'])){ $model->attributes=$_POST['Add']; $b->attributes=$_POST['Maturity']; $valid=$model->validate(); $valid=$b->validate() && $valid; if($valid){ if($model->save()){ $b->save(); $this->redirect(array('view','id'=>$model->Id)); } } } $this->render('update',array( 'model'=>$model, 'b'=>$b, )); }
Re @Rohit Suthar
@Rohit Suthar I got this error...
Call to undefined method stdClass::getErrors() in C:\xampp\htdocs\yii\framework\web\helpers\CHtml.php on line 2002
Were I put this Code in my controller
$model->getErrors() and $b->getErrors()
RE: #17714
$b = Maturity::model()->findByAttributes(array('code'=>$model->code));
Update your code like this -
if($model->save()){ if(!empty($b)){ $b->code = $model-->code; $b->save(); } $this->redirect(array('view','id'=>$model->Id)); }
Re @Rohit Suthar
No Error but It Shows empty page..
public function actionUpdate() { $model=$this->loadModel($id); $b = Maturity::model()->findByAttributes(array('code'=>$model->code)); if(isset($_POST['Add'], $_POST['Maturity'])){ $model->attributes=$_POST['Add']; $b->attributes=$_POST['Maturity']; $valid=$model->validate(); $valid=$b->validate() && $valid; if($valid){ if($model->save()){ if(!empty($b)){ $b->code = $model-->code; $b->save(); } $this->redirect(array('view','id'=>$model->Id)); } } $this->render('update',array( 'model'=>$model, 'b'=>$b, )); } }
RE: #17717
@Vijay
May be its return NULL value so record not match.
Please do -
Can you open a post thread, and show me your whole code?
RE: #17720
@Vijay man don't post code here, you need to post on forum.
validate
Where and how should I define this $validate or validate()? My page is giving the following text:
Fatal error: Call to a member function validate() on a non-object in
blalba.php on line 95
Griss
You have to call validate on an instance of your data model otherwise you will get "Call to a member function validate() on a non-object."
$model = new User(); if($model->validate()) { // blah blah }
RE #19337
@Griss
Please post your issue on forum section with your source code & provide a post link.
Also you can be use like this-
$first_model = new FirstModel(); $second_model = new SecondModel(); if($first_model->validate() && $second_model->validate()){ ... .. }
**From Above Method create has work fine thanks for support.
Update code as follows
Pc relation
'hdds' => array(self::HAS_MANY, 'Hdd', 'pcid'), 'user' => array(self::BELONGS_TO, 'User', 'userId'), 'rams' => array(self::HAS_MANY, 'Ram', 'pcid'),
Hdd Relation
'pc' => array(self::BELONGS_TO, 'Pc', 'pcid'),
Ram relation
'pc' => array(self::BELONGS_TO, 'Pc', 'pcid'),
public function actionUpdate($id)
{ $model=$this->loadModel($id); $model2 = Hdd::model()->findByAttributes(array('pcid'=>$id)); $model3 = Ram::model()->findByAttributes(array('pcid'=>$id)); // Uncomment the following line if AJAX validation is needed // $this->performAjaxValidation($model); if(isset($_POST['Pc'])) { $model->attributes=$_POST['Pc']; $model2->attributes=$_POST['Hdd']; $model3->attributes=$_POST['Ram']; $valid=$model->validate(); $valid=$model2->validate() && $valid; $valid=$model3->validate() && $valid; if($valid ) { $model->save(false); $model2->save(false); $model3->save(false); $this->redirect(array('view','id'=>$model->id)); }} $this->render('update',array( 'model'=>$model, 'model2'=>$model2, 'model3'=>$model3, )); }
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.