Yii2 中 DynamicFormWidget 的自定义验证不起作用
Custom validation on DynamicFormWidget in Yii2 not working
我正在尝试为 Yii2 中的 DynamicFormWidget 小部件添加自定义验证。有一个 DynamicFormWidget 小部件 'percentage',动态创建的所有 'percentage' 值的总和应该是 100。我添加了一个验证规则
public function checkPercentageTotal($attribute, $params){
foreach (Yii::$app->request->post()['Category'] as $percentage){
$percentage_values[]=$percentage['percentage'];
}
if( array_sum($percentage_values)<>100){
$this->addError($attribute, 'Total percentage should be 100');return false;
}
}
并添加了
public function rules()
{
return [
[['percentage'] ,'checkPercentageTotal'],
];
}
但未显示 error.The 代码正在 function checkPercentageTotal()
中执行
这是我的视图文件中的代码:
<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use wbraganca\dynamicform\DynamicFormWidget;
use kartik\date\DatePicker;
use kartik\select2\Select2;
use dosamigos\tinymce\TinyMce;
use yii\helpers\ArrayHelper;
use backend\models\Department;
$script = <<< JS
$(".dynamicform_wrapper").on("afterInsert", function(e, item) {
console.log("afterInsert");
var row = $(this).closest('td');
var row=jQuery(item).find('select:eq(1)');
row.val('5')
});
JS;
$this->registerJs($script);
?>
<br>
<div class="dynamicform_wrapper">
<?php $form = ActiveForm::begin(['id' => 'dynamic-form', ]); ?>
<div class="box box-primary box-solid">
<div class="box-header with-border">
<h3 class="box-title">Create Survey Form</h3>
</div><br>
<div class="col-sm-12">
<div class="row">
<div class="col-sm-2">
<?= $form->field($modelSurvey, 'form_id')->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-6">
<?= $form->field($modelSurvey, 'title')->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-2">
<?php
$data=ArrayHelper::map(Department::find()->all(), 'id', 'department');
echo '<label class="control-label">Department</label>';
echo Select2::widget([
'name' => 'depart',
'id' => 'department',
'theme' =>Select2::THEME_BOOTSTRAP,
'value' => isset($modelSurvey->depart) ? $modelSurvey->depart : [],
'data' => $data,
// 'initValueText' => isset($model->dept) ? $model->dept : [],
'options' => [
'placeholder' => Yii::t('app', 'Choose Department...'),
'multiple' => true,
],
'pluginOptions' => [
'tags' => true,
'allowClear' => true,
'width' => '100%'
],]);
?>
</div>
<div class="col-sm-2">
<?php
$data=array('1'=>'Staff','2'=>'Student');
echo '<label class="control-label">Type</label>';
echo Select2::widget([
'name' => 'type_id',
'id' => 'type_id',
'theme' =>Select2::THEME_BOOTSTRAP,
'value' => isset($modelSurvey->type_id) ? $modelSurvey->type_id : [],
'data' => $data,
// 'initValueText' => isset($model->dept) ? $model->dept : [],
'options' => [
'placeholder' => Yii::t('app', 'Choose Type...'),
// 'multiple' => true,
],
'pluginOptions' => [
'tags' => true,
'allowClear' => true,
'width' => '100%'
],]);
?>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<?= $form->field($modelSurvey, 'abstract')->widget(TinyMce::className(), [
'options' => ['rows' => 6],
'language' => 'en',
'clientOptions' => [
'plugins' => [
"advlist autolink lists link charmap print preview anchor",
"searchreplace visualblocks code fullscreen",
"insertdatetime media table contextmenu paste"
],
'toolbar' => "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
]
]); ?>
</div>
<div class="col-sm-6">
<?= $form->field($modelSurvey, 'ledger')->widget(TinyMce::className(), [
'options' => ['rows' => 6],
'language' => 'en',
'clientOptions' => [
'plugins' => [
"advlist autolink lists link charmap print preview anchor",
"searchreplace visualblocks code fullscreen",
"insertdatetime media table contextmenu paste"
],
'toolbar' => "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
]
]); ?>
</div>
</div>
</div>
<div class="padding-v-md">
<div class="line line-dashed"></div>
</div>
<div class="col-md-12">
<table class="table table-bordered table-striped">
<tbody class="container-items">
<?php foreach ($modelsCategory as $indexCategory => $modelCategory): ?>
<tr class="house-item" >
<?php
// necessary for update action.
if (! $modelCategory->isNewRecord) {
echo Html::activeHiddenInput($modelCategory, "[{$indexCategory}]id");
}
?>
<th colspan="5" bgcolor="#EC7063" style="display: inline-grid; width: 100%;">Categories</th>
<td style="display: inline-grid; width: 8%;"><?= $form->field($modelCategory, "[{$indexCategory}]percentage")->label(true)->textInput(['maxlength' => true]) ?></td>
<td style="display: inline-block; width: 10%;">
<button type="button" class="add-house btn btn-success btn-xs"><span class="glyphicon glyphicon-plus"></span></button>
<button type="button" class="remove-house btn btn-danger btn-xs"><span class="glyphicon glyphicon-minus"></span></button>
</td>
<td style="display: block;" >
<?= $this->render('_form-rooms', [
'form' => $form,
'indexCategory' => $indexCategory,
'modelsQuestion' => $modelsQuestion[$indexCategory],
]) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php DynamicFormWidget::end(); ?>
<div class="form-group">
<div style="padding-left: 1%;">
<?= Html::submitButton($modelSurvey->isNewRecord ? 'Create' : 'Update', ['class' => 'btn btn-lg btn-danger']) ?>
</div></div>
<?php ActiveForm::end(); ?>
CategoryModel 中的代码是
<?php
namespace backend\models;
use Yii;
/**
* This is the model class for table "category".
*
* @property int $id
* @property string $survey_id
*/
class Category extends \yii\db\ActiveRecord
{
/**
* @inheritdoc
*/
public static function tableName()
{
return 'category';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['survey_id', 'percentage'], 'required'],
[['survey_id' ], 'string', 'max' => 250],
[['percentage'] ,'checkPercentageTotal'],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'survey_id' => Yii::t('app', 'Survey ID'),
'percentage' => Yii::t('app', 'Percent'),
];
}
public function getQuestions()
{
return $this->hasMany(Question::className(), ['cat_id' => 'id']);
}
public function checkPercentageTotal($attribute, $params){
foreach (Yii::$app->request->post()['Category'] as $percentage){
$percentage_values[]=$percentage['percentage'];
}
if( array_sum($percentage_values)<>100){
$this->addError($attribute, 'Total percentage should be 100');return false;
}
}
}
SurveyModel 的代码是
<?php
namespace backend\models;
use Yii;
/**
* This is the model class for table "survey".
*
* @property int $id
* @property string $form_id
* @property string $dept
* @property string $title
* @property string $period
* @property string $abstract
* @property string $ledger
* @property string $apr_name
* @property string $apr_desg
* @property string $sem
* @property string $acad_yr
* @property string $sec
* @property string $qualification
*/
class Survey extends \yii\db\ActiveRecord
{
/**
* @inheritdoc
*/
/**===============================
* floated variable for survey form
*/
/**
* end here =====================
*/
public static function tableName()
{
return 'survey';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['form_id', 'title', 'abstract', 'ledger','date_created'], 'required'],
[['form_id'], 'string', 'max' => 1250],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'form_id' => Yii::t('app', 'Form ID'),
'title' => Yii::t('app', 'Title'),
'abstract' => Yii::t('app', 'Abstract'),
'ledger' => Yii::t('app', 'Ledger'),
'date_created' =>Yii::t('app', 'Date Created'),
'depart' =>Yii::t('app', 'Department'),
];
}
}
创建动作控制器的代码是
public function actionCreate()
{
$modelSurvey = new Survey;
$modelsCategory = [new Category];
$modelsQuestion = [[new Question]];
if ($modelSurvey->load(Yii::$app->request->post())) {
foreach($_POST['depart'] as $departindex => $departvalue){
$department[]=$departvalue;
}
$dep=implode(',',$department);
$modelsCategory = Model::createMultiple(Category::classname());
Model::loadMultiple($modelsCategory, Yii::$app->request->post());
// validate survey and category models
$valid[] = $modelSurvey->validate();
$valid[] = Model::validateMultiple($modelsCategory) && $valid;
if (isset($_POST['Question'][0][0])) {
foreach ($_POST['Question'] as $indexCategory => $questions) {
foreach ($questions as $indexQuestion => $question) {
$data['Question'] = $question;
$modelQuestion = new Question;
$modelQuestion->load($data);
$modelsQuestion[$indexCategory][$indexQuestion] = $modelQuestion;
$valid[] = $modelQuestion->validate();
}
}
}
if ($valid) {
$transaction = Yii::$app->db->beginTransaction();
try {
$today = date("Y-m-d H:i:s");
$modelSurvey->date_created=$today;
$modelSurvey->depart=$dep;
$modelSurvey->type_id=$_POST['type_id'];
if ($flag = $modelSurvey->save(false)) {
foreach ($modelsCategory as $indexCategory => $modelCategory) {
if ($flag === false) {
break;
}
$modelCategory->survey_id = $modelSurvey->id;
if (!($flag = $modelCategory->save(false))) {
break;
}
if (isset($modelsQuestion[$indexCategory]) && is_array($modelsQuestion[$indexCategory])) {
foreach ($modelsQuestion[$indexCategory] as $indexQuestion => $modelQuestion) {
$modelQuestion->cat_id = $modelCategory->id;
if (!($flag = $modelQuestion->save(false))) {
break;
}
}
}
}
}
if ($flag) {
$transaction->commit();
return $this->redirect(['view', 'id' => $modelSurvey->id]);
} else {
$transaction->rollBack();
}
} catch (Exception $e) {
$transaction->rollBack();
}
}
}
return $this->render('create', [
'modelSurvey' => $modelSurvey,
'modelsCategory' => (empty($modelsCategory)) ? [new Category] : $modelsCategory,
'modelsQuestion' => (empty($modelsQuestion)) ? [[new Question]] : $modelsQuestion,
]);
}
我认为自定义验证规则对动态表单小部件元素没有任何影响
我想这是主要问题:
$valid[] = $modelSurvey->validate();
$valid[] = Model::validateMultiple($modelsCategory) && $valid;
// ...
if ($valid) {
即使你所有的验证 returns false
,你也会得到 false
的数组,这将被视为 true
(非空数组 == = 真)。您可以尝试使用以下内容更改此代码:
$valid = Model::validateMultiple($modelsCategory) && $valid;
但老实说,您的代码确实很乱,需要进行认真的重构。您不应该直接在模型内部访问 GET 或 POST 数据,并且此操作和验证非常复杂。
对于这种情况,您应该创建单独的 SurveyForm
并且所有验证和保存都应该在其中。 checkPercentageTotal
验证器并不真正适合 Category
模型 - 现在您正在多次执行相同的验证(分别针对每个类别)。此验证可能应该在 SurveyForm
模型中完成,仅一次。
我正在尝试为 Yii2 中的 DynamicFormWidget 小部件添加自定义验证。有一个 DynamicFormWidget 小部件 'percentage',动态创建的所有 'percentage' 值的总和应该是 100。我添加了一个验证规则
public function checkPercentageTotal($attribute, $params){
foreach (Yii::$app->request->post()['Category'] as $percentage){
$percentage_values[]=$percentage['percentage'];
}
if( array_sum($percentage_values)<>100){
$this->addError($attribute, 'Total percentage should be 100');return false;
}
}
并添加了
public function rules()
{
return [
[['percentage'] ,'checkPercentageTotal'],
];
}
但未显示 error.The 代码正在 function checkPercentageTotal()
这是我的视图文件中的代码:
<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use wbraganca\dynamicform\DynamicFormWidget;
use kartik\date\DatePicker;
use kartik\select2\Select2;
use dosamigos\tinymce\TinyMce;
use yii\helpers\ArrayHelper;
use backend\models\Department;
$script = <<< JS
$(".dynamicform_wrapper").on("afterInsert", function(e, item) {
console.log("afterInsert");
var row = $(this).closest('td');
var row=jQuery(item).find('select:eq(1)');
row.val('5')
});
JS;
$this->registerJs($script);
?>
<br>
<div class="dynamicform_wrapper">
<?php $form = ActiveForm::begin(['id' => 'dynamic-form', ]); ?>
<div class="box box-primary box-solid">
<div class="box-header with-border">
<h3 class="box-title">Create Survey Form</h3>
</div><br>
<div class="col-sm-12">
<div class="row">
<div class="col-sm-2">
<?= $form->field($modelSurvey, 'form_id')->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-6">
<?= $form->field($modelSurvey, 'title')->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-2">
<?php
$data=ArrayHelper::map(Department::find()->all(), 'id', 'department');
echo '<label class="control-label">Department</label>';
echo Select2::widget([
'name' => 'depart',
'id' => 'department',
'theme' =>Select2::THEME_BOOTSTRAP,
'value' => isset($modelSurvey->depart) ? $modelSurvey->depart : [],
'data' => $data,
// 'initValueText' => isset($model->dept) ? $model->dept : [],
'options' => [
'placeholder' => Yii::t('app', 'Choose Department...'),
'multiple' => true,
],
'pluginOptions' => [
'tags' => true,
'allowClear' => true,
'width' => '100%'
],]);
?>
</div>
<div class="col-sm-2">
<?php
$data=array('1'=>'Staff','2'=>'Student');
echo '<label class="control-label">Type</label>';
echo Select2::widget([
'name' => 'type_id',
'id' => 'type_id',
'theme' =>Select2::THEME_BOOTSTRAP,
'value' => isset($modelSurvey->type_id) ? $modelSurvey->type_id : [],
'data' => $data,
// 'initValueText' => isset($model->dept) ? $model->dept : [],
'options' => [
'placeholder' => Yii::t('app', 'Choose Type...'),
// 'multiple' => true,
],
'pluginOptions' => [
'tags' => true,
'allowClear' => true,
'width' => '100%'
],]);
?>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<?= $form->field($modelSurvey, 'abstract')->widget(TinyMce::className(), [
'options' => ['rows' => 6],
'language' => 'en',
'clientOptions' => [
'plugins' => [
"advlist autolink lists link charmap print preview anchor",
"searchreplace visualblocks code fullscreen",
"insertdatetime media table contextmenu paste"
],
'toolbar' => "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
]
]); ?>
</div>
<div class="col-sm-6">
<?= $form->field($modelSurvey, 'ledger')->widget(TinyMce::className(), [
'options' => ['rows' => 6],
'language' => 'en',
'clientOptions' => [
'plugins' => [
"advlist autolink lists link charmap print preview anchor",
"searchreplace visualblocks code fullscreen",
"insertdatetime media table contextmenu paste"
],
'toolbar' => "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
]
]); ?>
</div>
</div>
</div>
<div class="padding-v-md">
<div class="line line-dashed"></div>
</div>
<div class="col-md-12">
<table class="table table-bordered table-striped">
<tbody class="container-items">
<?php foreach ($modelsCategory as $indexCategory => $modelCategory): ?>
<tr class="house-item" >
<?php
// necessary for update action.
if (! $modelCategory->isNewRecord) {
echo Html::activeHiddenInput($modelCategory, "[{$indexCategory}]id");
}
?>
<th colspan="5" bgcolor="#EC7063" style="display: inline-grid; width: 100%;">Categories</th>
<td style="display: inline-grid; width: 8%;"><?= $form->field($modelCategory, "[{$indexCategory}]percentage")->label(true)->textInput(['maxlength' => true]) ?></td>
<td style="display: inline-block; width: 10%;">
<button type="button" class="add-house btn btn-success btn-xs"><span class="glyphicon glyphicon-plus"></span></button>
<button type="button" class="remove-house btn btn-danger btn-xs"><span class="glyphicon glyphicon-minus"></span></button>
</td>
<td style="display: block;" >
<?= $this->render('_form-rooms', [
'form' => $form,
'indexCategory' => $indexCategory,
'modelsQuestion' => $modelsQuestion[$indexCategory],
]) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php DynamicFormWidget::end(); ?>
<div class="form-group">
<div style="padding-left: 1%;">
<?= Html::submitButton($modelSurvey->isNewRecord ? 'Create' : 'Update', ['class' => 'btn btn-lg btn-danger']) ?>
</div></div>
<?php ActiveForm::end(); ?>
CategoryModel 中的代码是
<?php
namespace backend\models;
use Yii;
/**
* This is the model class for table "category".
*
* @property int $id
* @property string $survey_id
*/
class Category extends \yii\db\ActiveRecord
{
/**
* @inheritdoc
*/
public static function tableName()
{
return 'category';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['survey_id', 'percentage'], 'required'],
[['survey_id' ], 'string', 'max' => 250],
[['percentage'] ,'checkPercentageTotal'],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'survey_id' => Yii::t('app', 'Survey ID'),
'percentage' => Yii::t('app', 'Percent'),
];
}
public function getQuestions()
{
return $this->hasMany(Question::className(), ['cat_id' => 'id']);
}
public function checkPercentageTotal($attribute, $params){
foreach (Yii::$app->request->post()['Category'] as $percentage){
$percentage_values[]=$percentage['percentage'];
}
if( array_sum($percentage_values)<>100){
$this->addError($attribute, 'Total percentage should be 100');return false;
}
}
}
SurveyModel 的代码是
<?php
namespace backend\models;
use Yii;
/**
* This is the model class for table "survey".
*
* @property int $id
* @property string $form_id
* @property string $dept
* @property string $title
* @property string $period
* @property string $abstract
* @property string $ledger
* @property string $apr_name
* @property string $apr_desg
* @property string $sem
* @property string $acad_yr
* @property string $sec
* @property string $qualification
*/
class Survey extends \yii\db\ActiveRecord
{
/**
* @inheritdoc
*/
/**===============================
* floated variable for survey form
*/
/**
* end here =====================
*/
public static function tableName()
{
return 'survey';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['form_id', 'title', 'abstract', 'ledger','date_created'], 'required'],
[['form_id'], 'string', 'max' => 1250],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'form_id' => Yii::t('app', 'Form ID'),
'title' => Yii::t('app', 'Title'),
'abstract' => Yii::t('app', 'Abstract'),
'ledger' => Yii::t('app', 'Ledger'),
'date_created' =>Yii::t('app', 'Date Created'),
'depart' =>Yii::t('app', 'Department'),
];
}
}
创建动作控制器的代码是
public function actionCreate()
{
$modelSurvey = new Survey;
$modelsCategory = [new Category];
$modelsQuestion = [[new Question]];
if ($modelSurvey->load(Yii::$app->request->post())) {
foreach($_POST['depart'] as $departindex => $departvalue){
$department[]=$departvalue;
}
$dep=implode(',',$department);
$modelsCategory = Model::createMultiple(Category::classname());
Model::loadMultiple($modelsCategory, Yii::$app->request->post());
// validate survey and category models
$valid[] = $modelSurvey->validate();
$valid[] = Model::validateMultiple($modelsCategory) && $valid;
if (isset($_POST['Question'][0][0])) {
foreach ($_POST['Question'] as $indexCategory => $questions) {
foreach ($questions as $indexQuestion => $question) {
$data['Question'] = $question;
$modelQuestion = new Question;
$modelQuestion->load($data);
$modelsQuestion[$indexCategory][$indexQuestion] = $modelQuestion;
$valid[] = $modelQuestion->validate();
}
}
}
if ($valid) {
$transaction = Yii::$app->db->beginTransaction();
try {
$today = date("Y-m-d H:i:s");
$modelSurvey->date_created=$today;
$modelSurvey->depart=$dep;
$modelSurvey->type_id=$_POST['type_id'];
if ($flag = $modelSurvey->save(false)) {
foreach ($modelsCategory as $indexCategory => $modelCategory) {
if ($flag === false) {
break;
}
$modelCategory->survey_id = $modelSurvey->id;
if (!($flag = $modelCategory->save(false))) {
break;
}
if (isset($modelsQuestion[$indexCategory]) && is_array($modelsQuestion[$indexCategory])) {
foreach ($modelsQuestion[$indexCategory] as $indexQuestion => $modelQuestion) {
$modelQuestion->cat_id = $modelCategory->id;
if (!($flag = $modelQuestion->save(false))) {
break;
}
}
}
}
}
if ($flag) {
$transaction->commit();
return $this->redirect(['view', 'id' => $modelSurvey->id]);
} else {
$transaction->rollBack();
}
} catch (Exception $e) {
$transaction->rollBack();
}
}
}
return $this->render('create', [
'modelSurvey' => $modelSurvey,
'modelsCategory' => (empty($modelsCategory)) ? [new Category] : $modelsCategory,
'modelsQuestion' => (empty($modelsQuestion)) ? [[new Question]] : $modelsQuestion,
]);
}
我认为自定义验证规则对动态表单小部件元素没有任何影响
我想这是主要问题:
$valid[] = $modelSurvey->validate();
$valid[] = Model::validateMultiple($modelsCategory) && $valid;
// ...
if ($valid) {
即使你所有的验证 returns false
,你也会得到 false
的数组,这将被视为 true
(非空数组 == = 真)。您可以尝试使用以下内容更改此代码:
$valid = Model::validateMultiple($modelsCategory) && $valid;
但老实说,您的代码确实很乱,需要进行认真的重构。您不应该直接在模型内部访问 GET 或 POST 数据,并且此操作和验证非常复杂。
对于这种情况,您应该创建单独的 SurveyForm
并且所有验证和保存都应该在其中。 checkPercentageTotal
验证器并不真正适合 Category
模型 - 现在您正在多次执行相同的验证(分别针对每个类别)。此验证可能应该在 SurveyForm
模型中完成,仅一次。