Sometimes you need to handle multiple models of the same kind in a single form. For example, multiple settings, where
each setting is stored as a name-value pair and is represented by a Setting
active record model.
This kind of form is also often referred to as "tabular input".
In contrast to this, handling different models of different kind, is handled in the section
Complex Forms with Multiple Models.
The following shows how to implement tabular input with Yii.
There are three different situations to cover, which have to be handled slightly different:
In contrast to the single model forms explained before, we are working with an array of models now. This array is passed to the view to display the input fields for each model in a table like style and we will use helper methods of yii\base\Model that allow loading and validating multiple models at once:
Let's start with the controller action:
<?php
namespace app\controllers;
use yii\web\Controller;
use app\models\Setting;
class SettingsController extends Controller
{
// ...
public function actionUpdate()
{
$settings = Setting::find()->indexBy('id')->all();
if ($this->request->isPost) {
if (Setting::loadMultiple($settings, $this->request->post()) && Setting::validateMultiple($settings)) {
foreach ($settings as $setting) {
$setting->save(false);
}
return $this->redirect('index');
}
}
return $this->render('update', ['settings' => $settings]);
}
}
In the code above we're using indexBy() when retrieving models from the database to populate an array indexed by models primary keys.
These will be later used to identify form fields. Model::loadMultiple() fills multiple
models with the form data coming from POST
and Model::validateMultiple() validates all models at once.
As we have validated our models before, using validateMultiple()
, we're now passing false
as
a parameter to save() to not run validation twice.
Now the form that's in update
view:
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
$form = ActiveForm::begin();
foreach ($settings as $id => $setting) {
echo $form->field($setting, "[$id]value")->label($setting->name);
}
echo Html::submitButton('Save');
ActiveForm::end();
Here for each setting we are rendering name and an input with a value. It is important to add a proper index to input name since that is how Model::loadMultiple() determines which model to fill with which values.
Creating new records is similar to updating, except the part, where we instantiate the models:
public function actionCreate()
{
$settings = [];
if ($this->request->isPost) {
$count = count($this->request->post($setting->tableName()));
for ($i = 0; $i < $count; $i++) {
$settings[$i] = new Setting();
}
if (Setting::loadMultiple($settings, $this->request->post()) && Setting::validateMultiple($settings)) {
foreach ($settings as $setting) {
$setting->save(false);
}
return $this->redirect('index');
}
}
$settings[] = new Setting();
return $this->render('create', ['settings' => $settings]);
}
Here we create an initial $settings
array containing one model by default so that always at least one text field will be
visible in the view. Additionally we add more models for each line of input we may have received.
In the view you can use JavaScript to add new input lines dynamically.
Note: This section is under development.
It has no content yet.
TBD
Found a typo or you think this page needs improvement?
Edit it on github !
I put here a complete master-detail that encouraged me to try it. There is an important detail that we have to do in the controller when receiving the Ajax validation requests of the form that contains the Parent Model and the child models.
The case; I have a model Parent Class named 'Docbotellas' with a group of childs models whose class name is 'Detdocbotellas', The link Field in 'Detdocbotellas is 'doc_id' . I'll try colect tabular input
In the controler:
In the view :
<?php $form =\yii\bootstrap\ActiveForm::begin([ 'enableAjaxValidation'=> true,'id'=>'tabular-botellas' ]); ?> <div class="col-lg-3 col-md-4 col-sm-6 col-xs-12"> <?= $form->field($model, 'number')->textInput() ?> </div> <div class="col-lg-3 col-md-4 col-sm-6 col-xs-12"> <?= $form->field($model, 'weight')->textInput(['maxlength' => true]) ? </div> <label for="items" class="control-label">Items</label> <div class="table-responsive"> <table class="table table-bordered" id="items"> <thead> <tr style="background-color: #ddd;"> <th width="5%" class="text-center">Acciones</th> <th width="15%" class="text-left">Code</th> <th width="80%" class="text-left">Description</th> </tr> </thead> <tbody> <?php $orden=0; foreach($items as $item){ ?> <?php echo $this->render('item', ['form'=>$form,'item'=>$item,'orden'=>$orden]); $orden+=1; ?> <?php } ?> <tr id="addItem"> <td class="text-center"><button type="button" id="button-add-item" data-toggle="tooltip" title="Añadir" class="btn btn-xs btn-primary" data-original-title="Añadir"><i class="fa fa-plus"></i></button></td> <td class="text-right" colspan="2"></td> </tr> </tbody> </table> </div> <div class="form-group"> <?= Html::submitButton(Yii::t('names.labels', 'Save'), ['class' => 'btn btn-success']) ?> </div> <?php \yii\bootstrap\ActiveForm::end(); ?> </div>
The view 'item'
<?= $form->field($item,"[$orden]codigo")->label(false);?> <?= $form->field($item,"[$orden]descripcion")->label(false); ?> <?= $form->field($item, "[$orden]id")->hiddenInput(['value' => $item->id])->label(false);?>Signup or Login in order to comment.