YII2 : Add Dynamic form fields and their validations

WpTricks24 picture WpTricks24 · Jul 8, 2015 · Viewed 23k times · Source

I am adding dynamic form fields onChange of dropdown. Both types of fields are coming from different models and go to the database in different tables. I have already defined validation rules in models.

But validation are not working properly. My code is as follows:

Model :

<?php

namespace common\models;

use Yii;

/**
 * This is the model class for table "{{%membership_features}}".
 *
 * @property integer $id
 * @property string $title
 * @property string $type
 * @property integer $is_new
 * @property integer $status
 * @property integer $is_deleted
 * @property string $created_date
 * @property string $modified_date
 *
 * @property MembershipFeaturesValue[] $membershipFeaturesValues
 */
class MembershipFeatures extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */

    public $value=[];
    public static function tableName()
    {
        return '{{%membership_features}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['title', 'type', 'value','is_new', 'status'], 'required'],
            ['value', 'each', 'rule' => ['integer']],
            ['value', 'each', 'rule' => ['required']],
            [['is_new', 'status', 'value','is_deleted'], 'integer'],
            [['created_date', 'modified_date'], 'safe'],
            [['title', 'type'], 'string', 'max' => 255]
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => Yii::t('app', 'ID'),
            'title' => Yii::t('app', 'Title'),
            'type' => Yii::t('app', 'is boolean or value'),
            'is_new' => Yii::t('app', 'Is New'),
            'status' => Yii::t('app', 'Status'),
            'is_deleted' => Yii::t('app', 'Is Deleted'),
            'created_date' => Yii::t('app', 'Created Date'),
            'modified_date' => Yii::t('app', 'Modified Date'),
        ];
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getMembershipFeaturesValues()
    {
        return $this->hasMany(MembershipFeaturesValue::className(), ['feature_id' => 'id']);
    }
}

Controller :

<?php

namespace backend\controllers;

use Yii;
use common\models\MembershipFeatures;
use backend\models\MembershipFeaturesSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\web\Response;
use common\models\MembershipFeaturesValue;
use common\components\Helper;
/**
 * MembershipFeaturesController implements the CRUD actions for MembershipFeatures model.
 */
class MembershipFeaturesController extends Controller
{
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'delete' => ['post'],
                ],
            ],
        ];
    }

    /**
     * Lists all MembershipFeatures models.
     * @return mixed
     */
    public function actionIndex()
    {
        $searchModel = new MembershipFeaturesSearch();
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);

        return $this->render('index', [
            'searchModel' => $searchModel,
            'dataProvider' => $dataProvider,
        ]);
    }

    /**
     * Displays a single MembershipFeatures model.
     * @param integer $id
     * @return mixed
     */
    public function actionView($id)
    {
        return $this->render('view', [
            'model' => $this->findModel($id),
        ]);
    }

    /**
     * Creates a new MembershipFeatures model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @return mixed
     */
    public function actionCreate()
    {
        $model = new MembershipFeatures();
        $membershipPlan = \common\models\MembershipPlan::allPlans();

        if(isset($_GET['type'])){
            $model->type =$_GET['type'];
        }
        if (Yii::$app->request->isAjax  && $model->load(Yii::$app->request->post())) {

            Yii::$app->response->format = Response::FORMAT_JSON;
            return \yii\widgets\ActiveForm::validate($model);
        }

        if ($model->load(Yii::$app->request->post()) ) {

            if( $model->save()){   
                foreach ($membershipPlan as $key=>$value) {
                    $feature = new MembershipFeaturesValue();
                    $feature->feature_id = $model->id;
                    $feature->plan_id = $key;
                    $feature->value =$model->value[$key];
                    $feature->save();
                }
            }



            return $this->redirect(['index']);
        } 
            return $this->render('create', [
                'model' => $model,
                'membershipPlan'=>$membershipPlan,

            ]);

    }

    /**
     * Updates an existing MembershipFeatures model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param integer $id
     * @return mixed
     */
    public function actionUpdate($id)
    {    
        $membershipPlan = \common\models\MembershipPlan::allPlans();    
        $model = $this->findModel($id);

        $selected = MembershipFeaturesValue::find()->where(['feature_id'=>$model->id])->all();
        foreach ($selected as $key => $value) {
                $model->value[$value->plan_id]=$value->value;
        }    

        if(isset($_GET['type'])){
            $model->type =$_GET['type'];
        }
        if(Yii::$app->request->isAjax  && $model->load(Yii::$app->request->post())) {

            Yii::$app->response->format = Response::FORMAT_JSON;
            return \yii\widgets\ActiveForm::validate($model);
        }

        if ($model->load(Yii::$app->request->post()) ) {

            if( $model->save()){  

                foreach ($membershipPlan as $key=>$value) {
                    $feature = MembershipFeaturesValue::find()->where(['feature_id'=>$model->id,'plan_id'=>$key])->one();
                    $feature->value =$model->value[$key];
                    $feature->save();
                }
            }



            return $this->redirect(['index']);
        }  

        return $this->render('update', [
                'model' => $model,
                'membershipPlan'=>$membershipPlan,

            ]);

    }

    /**
     * Deletes an existing MembershipFeatures model.
     * If deletion is successful, the browser will be redirected to the 'index' page.
     * @param integer $id
     * @return mixed
     */
    public function actionDelete($id)
    {
        Helper::partialDelete('MembershipFeatures',$id);

        return $this->redirect(['index']);
    }

    /**
     * Finds the MembershipFeatures model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param integer $id
     * @return MembershipFeatures the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($id)
    {
        if (($model = MembershipFeatures::findOne($id)) !== null) {
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }
}

Form :

<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\widgets\Pjax;
use yii\helpers\Url;
/* @var $this yii\web\View */
/* @var $model common\models\MembershipFeatures */
/* @var $form yii\widgets\ActiveForm */
?>

<div class="membership-features-form">

    <?php $form = ActiveForm::begin([   
    'enableAjaxValidation' => true,
    'enableClientValidation'=>true,
    'validateOnSubmit'=>true,   
    'options' => ['data-pjax'=>true]]); ?>
    <?= $form->errorSummary($model); ?>
    <?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>

    <?= $form->field($model, 'type')->dropDownList(['boolean'=>'Boolean','value'=>'Value'],
        [
        'onchange'=>'
            $.pjax.reload({
            url: "'.Url::to(['create']).'?type="+$(this).val(),
            container: "#pjax-memfeature-form",
            timeout: 1000,
            });
        ',

        'class'=>'form-control',
        'prompt' => 'Select Type Of Value'
        ]) ?>

        <?php  Pjax::begin(['id'=>'pjax-memfeature-form','enablePushState'=>false]);     ?> 
        <?php 
            if($model->type==='boolean'){
                foreach ($membershipPlan as $key => $value) {
                    echo $form->field($model, "value[$key]")->checkbox(array(
                                'label'=>"$value",
                                'labelOptions'=>array('style'=>'padding:5px;'),

                                )); 
                }    
            }
            if($model->type==='value'){
                foreach ($membershipPlan as $key => $value) {
                  echo $form->field($model, "value[$key]")->textInput()->label("$value"); 
                }
            }
        ?>

         <?php Pjax::end(); ?>        
    <?= $form->field($model, 'is_new')->dropDownList(['0'=>'No','1'=>'Yes']) ?>

    <?= $form->field($model, 'status')->dropDownList(['1'=>'Active','0'=>'Inactive']) ?>

    <div class="form-group">
        <?= Html::submitButton($model->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
         <?= Html::a(Yii::t('app', 'Cancel'), ['/membership-features/'], ['class' => 'btn btn-danger']) ?>
    </div>

    <?php ActiveForm::end(); ?>

</div>

I want to validate my value field which is dynamically added when I change the type dropdown using Pjax. Please guide me a correct method for validating dynamically added form fields.

Answer

Andrew Blake picture Andrew Blake · Aug 7, 2015

Its been a month so guessing this has been solved but for reference for others like me looking for the same and to save having to step thru the framework to find the answer perhaps try something like:

...
use yii\helpers\Json;
...

<?php foreach ($form->attributes as $attribute) {
    $attribute = Json::htmlEncode($attribute);
    $this->registerJs("jQuery('form').yiiActiveForm('add', $attribute);");
} ?>

<?php Pjax::end(); ?>
...

Tested with regards to clientValidation and not with above question so have hacked my solution to hopefully answer above question.