📜 ⬆️ ⬇️

Yii: Dynamic change of validation rules (scripts)

In this little topic, I would like to tell you about one very simple recipe (which is probably familiar to many of you) in the context of the Yii framework. This is a dynamic change to the form validation rules — when the validation rules change depending on the user’s choice of your application, for example, by choosing a value from a list or a check box.

I was prompted to use this solution by a practical need when writing one simple Contact Center with a web interface. So as not to invent examples, I will briefly explain the use of this recipe on the example of the same KC. As is known, the main activity of CC operators is in receiving calls from customers and outgoing calls to customers. According to the results of the call, the operator must enter certain data into the system - the result of the conversation with the client, on the basis of which the status of the ticket and the subsequent logic of working with it (assigning a repeated call, any other procedure, closing the ticket, etc.) are determined. The problem is that some form fields (read, model attributes) must be filled in or unfilled depending on the “parent” status. For example, it is obvious that the “Result of the conversation with the customer” field does not make sense to fill in if the technical status of the call is “Under Call / Wrong Number” (the operator must give the technical status, not the system, since the call may occur, but not a client - an error in the room, etc.). At the same time, if the technical status “Successful dialing” is set, the field “The result of the conversation with the customer” must be filled. Again: depending on the outcome of the conversation, it may be necessary to fill in additional fields - for example, the fields “Date of repeated call” with the status of “Retry Call”.

Naturally, I would like the rules of validation for the entire form to change depending on the value of a specific form field. In practice, this task is very easy to use, but very useful in cases like mine.

So:
1. Create several scenarios for vadidatsii call form.
First, create rules for those fields that are required in all scenarios:
public function rules(){ //... array('call_status', 'required', 'on'=>'callSubmit, invalidNumberSubmit, validCallSubmit, successCallSubmit...' //... 'message'=>' "{attribute}"    .'), } 

I think the first rule does not cause questions - the technical status of the call is the value of the top level in the form - it is necessary in any scenario.
Continue to go from the general to the particular:
 //.. Inside rules() method array('talk_status', 'required', 'on'=>'successCallSubmit, secondCall,...') /*   ,        */ 

Next, we define the rules for more specific cases:
 //.. Inside rules() method array('new_call_date', 'required', 'on'=>'secondCall') /*     " "*/ 

')
Thus, we can go through all the necessary validation rules - both in general cases and in each particular case. The matter remains for small: to make the validation rules vary depending on the selected values ​​in the form. As you have probably guessed, for this we will use CActiveForm, since this widget out of the box allows you to simply and clearly perform ajax-validation.
For example, let's create a simple form:

  <?php $form=$this->beginWidget('CActiveForm', array( 'id'=>'call-submit-form', 'enableAjaxValidation'=>true, 'clientOptions'=>array( 'validateOnChange'=>true, 'validateOnSubmit'=>true ), //   )); ?> 


Of the interesting options we need to note these: 'enableAjaxValidation', 'validateOnChange', 'validateOnSubmit'. I think the purpose of these options does not cause questions and is clear from their names. We continue:

 <div class='control-group'> <?php echo $form->labelEx($model,'call_status'); ?> <?php echo $form->dropDownList($model,'call_status', CHtml::listData( CallStatuses::model()->findAll(),'id','title' ) ); ?> <?php echo $form->labelEx($model,'talk_status'); ?> <?php echo $form->dropDownList($model,'talk_status', CHtml::listData(TalkStatuses::model()->findAll(),'id','title' ), )); ?> <?php echo $form->error($model,'call_status'); ?> <?php echo $form->error($model,'talk_status'); ?> </div> <div class='control-group'> <?php echo $form->label($model,'new_call_date'); ?> <?php //     $this->widget('zii.widgets.jui.CJuiDatePicker', array( 'attribute'=>'new_call_date', 'model'=>$model, //... ), )); ?> </div> 


I will not bore you with a large number of fields, I think three is enough for an example. All that is left for us to do is actually implement the functionality for dynamically changing the validation rules (read the script).

In fact, this functionality is based on the simplest logic: you need to compare each field value with a script that the form will validate upon selecting this value (in this case, when choosing a value from the list created by the CActiveForm :: dropDownList () method). This is a very simple solution, and I will not dwell on the details and create implementation in the OOP-style, because the main thing is to convey the idea.

Instead, for simplicity, I will put the appropriate code directly into the controller's class performAjaxValidation () method. Below is a slightly modified code for the standard performAjaxValidation () method and a method that takes the form:

 //Inside controller public function actionSaveCall(){ /*     'callSubmit',       - 'call_status' -   . */ $model=new Calls('callSubmit'); $this->performCallAjaxValidation($model); if( isset($_POST['Calls']) ) { $model->attributes = $_POST['Calls']; if( $model->save() ) $this->redirect( Yii::app()->user->returnUrl ); } } protected function performAjaxValidation($model) { if(isset($_POST['ajax']) && $_POST['ajax']==='calls-form') { $callStatusesScenarios = array( Calls::CALL_FAIL=>'validCallSubmit', Calls::SUCCESS_CALL=>'successCallSubmit', Calls::WRONG_NUMBER=>'invalidNumberSubmit'); if( !empty($_POST['Calls']['call_status']) && !empty( $callStatusesScenarios[ $_POST['Calls']['call_status'] ] ) ){ $model->setScenario ( $callStatusesScenarios[ $_POST['Calls']['call_status'] ] ); } echo CActiveForm::validate($model); Yii::app()->end(); } } 


As you can see from the code, all I do is simply change the script depending on the incoming value of the 'call_status' field. Now, if the operator chooses the “Successful dialing” status on the form, the model script will change to 'successCallSubmit', which now expects the form to have a call status, and as a result, by selecting “Successful dialing”, the operator will not be able to send the form, until the "Result of the conversation" field is filled. At the same time, if the status of the dialer is “Wrong number”, the form will be added without any additional fields. By the same principle, it is possible to act further - with the status of conversations and their additional fields, etc.

The application of this approach will be useful in such situations, when the number and types of fields required for filling will vary depending on the user's choice. I apologize for a little puns, and success to you in developing on the wonderful framework Yii.

Source: https://habr.com/ru/post/146589/


All Articles