In this article we are going to create a dynamic form. It is a form that keeps its structure and rules in configuration arrays, not in hard-coded classes.
We are going to use the DynamicModel.
From the Yii documentation:
DynamicModel is a model class primarily used to support ad hoc data validation.
app/models/DynamicModel.php
:<?php namespace app\models; use \yii\base\DynamicModel; class MyDynamicModel extends DynamicModel { public $attributeLabels = []; public function attributeLabels(){ return $this->attributeLabels; } public function setAttributeLabel($attribute, $label) { $this->attributeLabels[$attribute] = $label; } }
In a controller file (e.g. controllers/SiteController.php
), create an action
method.\
First, we are creating the _field_ definitions, then the _field rules_:
<?php use app\models\MyDynamicModel; public function actionShowForm() { $fields = [ // {{{ field definitions 'name'=>[ 'type'=>'text', 'label'=>'Name', 'hint' => 'Bitte geben Sie Ihren Familiennamen ein', ], 'vorname'=>[ 'type'=>'text', 'label'=>'Vorname', ], 'geburtsname'=>[ 'type'=>'text', 'label'=>'Geburtsname', ], 'strasse'=>[ 'type'=>'text', 'label'=>'Strasse', ], 'email'=>[ 'type'=>'text', 'label'=>'E-Mail', ], 'verheiratet' => [ 'type'=>'checkbox', 'label' => 'Verheiratet', ], 'mypassword'=>[ 'type'=>'password', 'label'=>'Password', ], 'sex'=>[ 'type'=>'radioList', 'label'=>'Geschlecht', 'options'=>['m'=>'männlich', 'f'=>'weiblich'], 'inline'=>true, ], 'testDropdown' => [ 'type'=>'dropdownList', // 'label'=>'Geschlecht', 'options'=>[1=>'eins', 2=>'zwei'], ], ]; // }}} $fieldRules = [ // {{{ field rules [['name', 'vorname', 'strasse', 'email'], 'required'], [['name', 'email'], 'string', 'max'=>128], ['email', 'email'], [['verheiratet'], 'boolean'], [['Geburtsname', 'sex'], 'safe'], ['testDropdown', 'number', 'integerOnly'=>true], ]; // }}} }
The $fields
array is an array of field attributes, indexed by the field names.
As type
, we have:
The fieldRules
array is defined as described in the Guide: Getting Data from Users/Validating Input/Declaring Rules.
We the are going to initialize the model, the checkboxes and the labels:
$model = new MyDynamicModel(array_keys($fields)); // Init checkbox attributes $checkboxes = array(); // {{{ Init labels foreach($fields as $field=>$settings) { if(!empty($settings['label'])) $model->setAttributeLabel($field, $settings['label']); else $model->setAttributeLabel($field, \yii\helpers\Inflector::camel2words($field, true)); if($settings['type']=='checkbox') $checkboxes[] = $field; } // }}}
We also add the rules we defined earlier, and we initialize some default values for the model attributes:
// {{{ Add rules to DynamicModel foreach($fieldRules as $settings) { $attributes = array_shift($settings); $validator = array_shift($settings); $model->addRule($attributes, $validator, $settings); } // }}} // Init default values $model->attributes = array( // {{{ 'name' => 'Werner', 'vorname' => 'Joachim', 'strasse' => 'Am Wingert 10', 'verheiratet' => true, 'sex' => 'm', ); // }}}
Finally, we render the form:
return $this->render('form', [ 'model' => $model, 'fields' => $fields, ]);
Create the form view file in views/site/form.php
:
<?php use yii\helpers\Html; use yii\bootstrap\ActiveForm; use yii\web\View; ?> <div class="site-form"> <h1><?= Html::encode($this->title) ?></h1> <?php $form = ActiveForm::begin([ 'id' => 'my-form', 'layout' => 'horizontal', // 'options' => ['class' => 'form-horizontal'], ]); ?> <?= $form->errorSummary($model) ?> <?php $yesNoOptions = [1=>'Ja', 0=>'Nein']; foreach($model->attributes() as $field) { $input = null; switch($fields[$field]['type']) { case 'text': $input = $form->field($model, $field)->textInput(); break; case 'textarea': $textareaOptions = isset($fields[$field]['options']) ? $fields[$field]['options'] : array(); // $yesNoOptions; $input = $form->field($model, $field)->textarea($textareaOptions); break; case 'checkbox': $input = $form->field($model, $field)->checkbox(); break; case 'password': $input = $form->field($model, $field)->passwordInput(); break; case 'dropdownList': $dropdownOptions = isset($fields[$field]['options']) ? $fields[$field]['options'] : array(); // $yesNoOptions; $input = $form->field($model, $field)->dropdownList($dropdownOptions); break; case 'radioList': $dropdownOptions = isset($fields[$field]['options']) ? $fields[$field]['options'] : $yesNoOptions; $input = $form->field($model, $field); if(!empty($fields[$field]['inline']) and $fields[$field]['inline']==true) $input->inline(true); $input->radioList($dropdownOptions); break; default: $input = $form->field($model, $field)->textInput(); break; } if(!empty($fields[$field]['hint'])) $input->hint($fields[$field]['hint']); echo $input; } ?> <div class="form-group"> <div class="col-lg-offset-1 col-lg-11"> <?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?> </div> </div> <?php ActiveForm::end() ?> </div>
This view creates the ActiveForm
.
In a big switch case
block, the various form input controls are created.
In the controller file, add this code before the render
call:
// {{{ Evaluate posted data if ($model->load(Yii::$app->request->post()) && $model->validate()) { Yii::$app->session->setFlash('success', 'The form has been submitted.'); }