Yii 2.0 Scenarios - Dynamic Validation - validation

I have a situation where I would like to validate a textbox input; however, the type of validation is dependent on the selection of a dropdown list on that page (there would be multiple of each of these on a page). So, the textbox can take a string, an integer, a date, etc. and the user must select something from the dropdown list that would tell me what the data type has to be that the user will enter.
I was thinking to use scenarios for this; however, the textbox is 1 field in my model/database that accepts a string at the database level (I just don't want the user to enter a string when an integer is appropriate regardless of how I handle on the backend).
Here's what I was thinking so far, but I don't know if this is the best route to take for this problem? Also, if I can use the scenarios for this then how do I specify in the view the rules for the textbox when a user selects something that requires i.e..date input? Is there a way that I could call the rule with Ajax on click of dropdown list?? Sorry, this is probably a little too broad a question, but I'm new to Yii and I'm just trying to get headed down the right path with this. Thank you!
public function scenario()
{
return[
'isstring' => ['filter_value'],
'isint' => ['filter_value'],
'isdate' => ['filter_value'],
'isfloat' => ['filter_value'],
'all' => ['freq_int', 'freq_unit_fk', 'filter_field_fk', 'arithmetic_op', 'logic_op', 'filter_value']
];
}
public function rules()
{
return [
[['freq_int', 'freq_unit_fk'], 'integer', 'message' => 'Integer'],
[['freq_int', 'freq_unit_fk', 'filter_field_fk', 'arithmetic_op'], 'required'],
[['logic_op'], 'safe'],
[['filter_value'], 'integer', 'on' => 'isint'],
[['filter_value'], 'string', 'min' => '2', 'max' => '75', 'on' => 'isstring'],
[['filter_value'], 'trim', 'on' => 'isstring'],
[['filter_value'], 'number', 'on' => 'isfloat'],
[['filter_value'], 'date', 'on' => 'isdate']
];
}
Ok, so to give more detail, and I've decided to drop the idea of having the validation set based on a dropdown selection for now. Instead, it just needs to get set based on a value that I'm getting from the database when the page gets loaded. I have a partial view file called '_settings.php' and it gets rendered as a modal through a view file called 'manage.php'. Here is the code in _settings.php that displays the text boxes that I'm trying get the validation to work on:
foreach($crystalfields as $field)
{
if($field['custom'] == 4)
{
$datatype = $model->getDatatypeName($field['datatype']);
$filterstring = $form->field($model, 'filter_value', ['inputOptions' => ['id' =>'filterval'.$crystalid.'-'.$field['fieldid']]])
->textinput(['name' => 'filterval'.$crystalid.'-'.$field['fieldid']])
->label(false);
$filterstrings = $filterstrings.'<div class="form-group">
<div class="form_group row">
<label for="filterval'.$crystalid.'-'.$field['fieldid'].'" class="col-sm-3 control-label">'.$field['fieldname'].'</label>
<div class="col-sm-6 pull-right">'.$filterstring.'</div>
</div>
</div>';
}
...there is more to the foreach loop that is irrelevent. Basically, what I want to do is have the validation for this text box be set based on the result of the method $model->getDatatypeName($field['datatype']) ... method is part of my model where the validation rules exist; however, the primary key $field['datatype'] that I'm passing to this method is based on a query that is joining many related tables in my view model ..$model in this case. So when this method passes back 'string' I want to set the validation accordingly. There can be multiple of these text boxes within the modal that each have different validation types but are attached to the same 'filter_value' field each time.

I think the easier solution here would be to use the when-functionality. This gives you fine control over whether the rule should be triggered:
public function rules()
{
return [
[['freq_int', 'freq_unit_fk'], 'integer', 'message' => 'Integer'],
[['freq_int', 'freq_unit_fk', 'filter_field_fk', 'arithmetic_op'], 'required'],
[['logic_op'], 'safe'],
[['filter_value'], 'integer', 'when' => function ($model) {return $model->logic_op == 'integer'; }],
[['filter_value'], 'string', 'min' => '2', 'max' => '75', 'when' => function ($model) {return $model->logic_op == 'string'; }],
[['filter_value'], 'trim', 'when' => function ($model) {return $model->logic_op == 'string'; }]],
[['filter_value'], 'number', 'when' => function ($model) {return $model->logic_op == 'float'; }]],
[['filter_value'], 'date', 'when' => function ($model) {return $model->logic_op == 'date'; }]]
];
}

Related

Property "BankMaster.swift_code" is not defined

I am having issues rendering a modal when i click on add new button in yii with these error
CException
Property "BankMaster.swift_code" is not defined
This the view file (BankMaster/_form.php)
<?php echo $form->textFieldRow($model, 'swift_code', array('labelOptions' => array('class' => 'col-md-8 bank-master-settings-cls'), 'class' => 'col-xs-12 col-sm-12 col-md-12 col-lg-12', 'maxlength' => 100)); ?>
swift_code is the changes i made.
This the Model file (BankMaster.php)
public function rules() {
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('full_name, short_name,iban_no,branch,payroll_status', 'required'),
array('full_name, address, email,swift_code', 'safe'),
array('opening_balance', 'numerical'),
array('ledger_code', 'uniqueCode'),
array('telephone,account_no, company_id', 'numerical', 'integerOnly' => true),
array('short_name,ledger_code', 'length', 'max' => 100),
array('full_name, email', 'length', 'max' => 100),
array('email', 'email'),
array('short_name', 'uniqueInLedger'),
// The following rule is used by search().
// #todo Please remove those attributes that should not be searched.
array('id, short_name,account_no,iban_no,opening_balance,op_balance_dc,branch_type,branch, full_name, address, telephone, email, payroll_status, is_default', 'safe', 'on' => 'search'),
);
}
swift_code is the changes i made too.
Then i added a column in erp_bank_master table
SOLUTION: "wp_105578" was the right database instead of "wpexp_dev"

Validate time in relation to a checkbox is checked or not?

I have 3 input fields (reservationdate, starttime, endtime) and 1 checkbox (holeday).
If the checkbox clicked I do not need starttime and endtime. On the other hand starttime and endtime is required.
What can I do to solve this task?
I tried in Laravel the required_if validation-function. But I'm certainly using it wrong
// ReservationController store:
$data = $request->validate([
'userid' => 'required|numeric',
'vehicleid' => 'required|numeric',
'budgetid' => 'required|numeric',
'reservationdate' => 'required|date',
'starttime' => 'required_if:holeday,on|date_format:H:i|before_or_equal:endtime',
'endtime' => 'date_format:H:i',
'holeday' => 'boolean'
]);
index.blade.php (only the checkbox)
<div class="input-field col s2">
<label>
<input type="checkbox" name="holeday" class="filled-in" />
<span>Hole Day</span>
</label>
</div>
If the checkbox is checked I get the error-message "The starttime field is required when holeday is on." but in this case I need no error. Hey user it is OK. I don´t need a starttime or endtime. Your clicked the holeday.
Correct me if I am mistaken: you are trying to have starttime and endtime required only when holeday is not checked.
If I recall correctly when a checkbox is unchecked or disabled, it isn't sent at all to the server, so you can verify for the presence/absence of that field name:
// ReservationController store:
$data = $request->validate([
'userid' => 'required|numeric',
'vehicleid' => 'required|numeric',
'budgetid' => 'required|numeric',
'reservationdate' => 'required|date',
'starttime' => 'required_without:holeday|date_format:H:i|before_or_equal:endtime',
'endtime' => 'required_without:holeday|date_format:H:i',
'holeday' => 'required_without_all:starttime,endtime|in:on'
]);
Note: boolean validations accepts only true, 1``and "1" as thruthy values. But if the checkbox is set without a value attribute you would get "on" value as default check value.
However if you pass all of the three fields with their respective values, the validation would still pass, because required doesn't ensure that if the condition is not met that field must not be present.
You can take that into account and just write your logic to check if holeday has "on" value then ignore starttime and endtime values and viceversa.
Otherwise, you have to use a custom validation rule like this one (untested):
Validator::extend('not_present_with', function ($attribute, $value, $parameters, $validator) {
foreach ($parameters as $parameter) {
if (! Arr:has($validator->attributes(), $parameter)) {
return false;
}
}
return ! Arr::has($validator->attributes(), $attribute);
});
Then use the following validation rules:
// ReservationController store:
$data = $request->validate([
'userid' => 'required|numeric',
'vehicleid' => 'required|numeric',
'budgetid' => 'required|numeric',
'reservationdate' => 'required|date',
'starttime' => 'not_present_with:holeday|required_without:holeday|date_format:H:i|before_or_equal:endtime',
'endtime' => 'not_present_with:holeday|required_without:holeday|date_format:H:i',
'holeday' => 'not_present_with:starttime,endtime|required_without_all:starttime,endtime|in:on'
]);

Validate Select2 in Yii2 via AJAX

I have Yii2 application which uses the Kartik plugin to initialize Select2 dropdowns in forms.
I have one particular Select2 which uses AJAX call to get the data for the drop down options.
<?=
$form->field($model, 'court_house_id', ['enableAjaxValidation' => true, 'selectors' => ['input' => '#' . $id . "-court-house"],'template' => FormHelper::GenerateFieldTemplate([6])])
->widget(Select2::classname(), [
'options' => ['id' => $id . "-court-house", 'placeholder' => Yii::t('app', 'Search court house...')],
'hashVarLoadPosition' => \yii\web\View::POS_READY,
'pluginOptions' => [
'dropdownParent' => new JsExpression("$('#$modalWindowId')"),
'allowClear' => true,
'minimumInputLength' => 2,
'language' => [
'errorLoading' => new JsExpression("function () { return '" . Yii::t('app', 'Search...') . "'; }"),
],
'ajax' => [
'url' => app\components\UrlMaker::link('data/court-house-list'),
'dataType' => 'json',
'data' => new JsExpression('function(params) { return {q:params.term}; }')
],
'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
'templateResult' => new JsExpression('function(courthouse) { return courthouse.text; }'),
'templateSelection' => new JsExpression('function (courthouse) { return courthouse.text;}'),
]])
->label(Yii::t('app', 'Court House'), ['class' => FormHelper::GenerateLabelClassTemplate([3])]);
?>
Intentionally pasting all of the code, although most of it is irrelevant I would assume.
I have this loaded in multiple dynamically created forms thus all the strange ids and selectors. However, the form has different dropdown which controls whether some of the fields are shown (and required) or not. This particular field above is only shown in one of the scenarios which all the other variations of the form do not have it. So the model has the following validation:
[['court_house_id', 'staff'], 'required', 'on' => self::SCENARIO_ONE],
By the way staff is just a regular text field and everything works for it.
In order to change the scenario, I have the following line in the view with the form:
<?php $model->scenario = \app\models\MyModel::SCENARIO_ONE; ?>
The problem is that when I submit the form empty, the staff field gets marked in red as invalid but the court house is marked in green as valid although it is empty.
If I go into the model and remove the 'on' => self::SCENARIO_ONE part then everything works as expected - on empty submit the court house field also lights up in red but that would be a problem for the rest of my scenarios where this field is not needed.
Any ideas what might be causing the problem and how to resolve it?
Try to set the scenario in controller before calling save() method, for example
$model = new MyModel(['scenario' => MyModel::SCENARIO_ONE])

Yii2 ActiveForm Ajax Validation and submit

I have made an Active form with validation and ajax submit.
The form is created in view:
<?php $form = ActiveForm::begin([
'id' => 'login-form',
'layout' => 'horizontal',
'method' => 'post',
'enableClientValidation' => false,
'enableAjaxValidation' => true,
'validationUrl' => 'panel/validate',
'fieldConfig' => [
'options' => [
'tag' => false,
],
'template' =>'{input}<p class="help-block help-block-error ">{error}</p>'
],
]); ?>
The validation action:
public function actionValidate()
{
$model = new LoginForm();
$request = \Yii::$app->getRequest();
if ($request->isAjax && $request->isPost && $model->load(Yii::$app->request->post())) {
\Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
return $this->renderAjax('login', [
'model' => $model,
]);
}
When i leave the fields blank for example, or do not specify the captcha i can see the ajax response:
{"loginform-username":["Username cannot be blank."],"loginform-password":["Password cannot be blank."],"loginform-captcha":["Captcha cannot be blank."]}
However, those errors not getting shown under my form fields. The form fields are created like this:
<?= $form->field($model, 'username')->textInput()
If i turn off ajax validation, the erros are displayed. What can be wrong here?
I'm afraid there is no possible way to display error while turning off 'tag' = false in fieldConfig.
Even though it works for server-side validation, the main problem is how yii.activeForm.js updateInput() function for fields works. When ajax request is completed, the .js tries to find an outer tag (of field) with .field-<model>-<attribute> class selector and fetch error-div children. As long as there is no outer .field tag, no error message is append to form.
I'm not 100% sure about it, but this is my understanding from debugging yii.activeForm.js functionality.
Actually, here is the similar question in yii2/issues, where SilverFire explains that there is no way to achieve this.
ActiveForm fieldConfig options tag=>false will render class attribute without any tag

Create custom validation for form entity

I am using the form builder to create an choice-field form an entity looking like this:
$form->add(
'existing_items','entity', array(
'label' => 'Artikel aus',
'class' => 'ProjectShoppinglistBundle:Item',
'empty_value' => 'Bitte einen Artikel auswählen',
'property' => 'name',
'query_builder' => function(EntityRepository $er) use ($options) {
return $er->createQueryBuilder('item')
->leftJoin('item.userItems', 'userItem')
->where('userItem.user = ' . $options['attr']['id'])
->orderBy('item.name', 'ASC');
},
'attr' => array(
'class' => 'form-control',
),
));
but I am using jquery to change the content of the dropdown, so I need to change the validation for the field, how can I achieve that the values in the form are valid for all elements in the items-table and not just the one's which are linked to my the userId used in the query?
This is necessary for my approach because I have a second dropdown where the user can define if he wants to see the items on his own list, of other lists or all items
I have already taken a look at this but I still don't really get it how I can use the eventListener to get the desired result.
If someone could give me a useful hint, I would appreciate this very much.

Resources