情况
我用Yii网站上的维基文章《收集表格输入》作为例子。
我认为我不需要针对多个模型验证传统意义上的表格输入。我只有一个模型,但我正在动态地创建表单中的字段数。
我正在导入CSV文件,其中不同文件的标题顺序不同。在正确解析文件之前,用户需要将哪个标头映射到哪个表/列。
我有一个单一的模型,从CFormModel
扩展而来的ImportParseForm
。它实际上只有一条规则:
public function rules()
{
return array(
array('header', 'required'),
);
}
以下是我的观点片段:
<?php foreach($headers as $h => $hItem): ?>
<div class="row">
<?php echo CHtml::label(CHtml::encode($hItem), "[$h]header"); ?> maps to
<?php echo $fParse->textField($mForm, "[$h]header"); ?>
<?php echo $fParse->error($mForm, "[$h]header"); ?>
</div>
<?php endforeach; ?>
这是我的控制器的一个片段:
$mForm = new ImportParseForm;
$valid = true;
if (isset($_POST['ImportParseForm'])){
foreach ($headers as $h => $hVal){
if (isset($_POST['ImportParseForm'][$h])){
$mForm->attributes = $_POST['ImportParseForm'][$h];
$valid = $mForm->validate() && $valid;
}
}
if ($valid){
// Process CSV
}
}
如果所有字段都有效,那么它将按预期通过。问题是,如果其中一个字段无效(或者在本例中为空),则所有字段都被标记为无效。
在Yii 1.1.10中,他们添加了CActiveForm::validateTabula(),但看起来它适用于多个模型。不完全是我这里的。但对于踢球,我在控制器中添加了以下内容(当然,删除了其他类型的验证):
CActiveForm::validateTabular($mForm, array('header'));
只有当填充了第一个元素时,表单本身才有效。如果填充了第一个元素,它将使用相同的值设置所有其他元素(并通过验证)。
问题
基本上,我可以使用CActiveForm对动态生成的字段(类似于表格输入,但只有一个模型)进行验证吗?
我试图做类似的事情,这是我在模型更新表单的情况下的解决方案。在这里,我利用模型验证来更新值更改事件的模型属性,它不需要提交按钮,看起来简单而花哨。这是代码片段。。。
视图代码:
<?php foreach($modelArray as $model): ?>
<div class="row">
<?php echo $form->textField($model, "[$model->id]attributeName"); ?>
<?php echo $form->label($model, "[$model->id]attributeName"); ?>
<?php echo $form->error($model, "[$model->id]attributeName"); ?>
</div>
<?php endforeach; ?>
控制器代码:
objArray = array();
foreach($_REQUEST['ModelName'] as $id => $attributes){
$obj = ModelName::model()->findByPk($id);
$obj->attributes = $attributes;
$obj->save();
$objArray[$id] = $obj;
}
echo CActiveForm::ValidateTabular($objArray);
Yii::app()->end();
在仔细阅读了收集表格输入之后,我使用了"多个"模型。我误解了多个模型意味着多个不同的结构化模型,而不仅仅是数组中多个相同的结构化模型。例如,在wiki中有一个片段显示了要更新的项目(模型数组):$items=$this->getItemsToUpdate();
。我纠正的假设是,该特定方法获取多个相同结构模型,但主键不同。。。或不同的记录。理解了这一点,文章的其余部分更有意义;)
但这是我关于如何创建CSV头映射表单的模型解决方案
class ImportParseForm extends CFormModel{
// Model really only has one attribute to check against, the header
var $header;
// New attributeLabels collected and stored on class instantiation
protected $attributeLabels;
// Modify construct so we can pass in custom attribute labels
public function __construct($attributeLabels = '', $scenario = '')
{
if (! is_array($attributeLabels)){
$this->attributeLabels = array($attributeLabels);
}
else{
$this->attributeLabels = $attributeLabels;
}
parent::__construct($scenario);
}
public function rules()
{
return array(
array('header', 'required'),
);
}
public function attributeLabels()
{
// Default mapping
$arr = array(
'header' => 'Header Mapping',
);
// Merge mapping where custom labels overwrite default
return array_merge($arr, $this->attributeLabels);
}
}
这里是我的控制器中的一个片段,关于我的等价$items=$this->getItemsToUpdate();
(同样,目标是收集一组模型)看起来像
// Get the first row of CSV, assume it's the headers
$tmpCsvRow = explode("'n", $mTmp->data);
$headers = explode(',', $tmpCsvRow[0]);
foreach ($headers as $header){
if (! empty($header)){ // Blank headers are lame, skip them
// Add a new model for each CSV header found into $mForm array
// You can also add in a custom attributeLabel, $header is an actual header name like 'First Name',
// so the new label for the header attribute in ImportParseForm would be 'First Name header' and
// it will show up properly in your CActiveForm view
$mForm[] = new ImportParseForm(array('header' => $header.' header'));
}
}
将$mForm
推送到您的视图中。现在,在您的视图中,为您的表单迭代$mForm
,如下所示(类似于wiki文章,但我在这里使用的是CActiveForm小部件):
<?php foreach($mForm as $m => $mItem): ?>
<div class="row">
<?php echo $fParse->labelEx($mItem,"[$m]header"); ?> maps to
<?php echo $fParse->textField($mItem, "[$m]header"); ?>
<?php echo $fParse->error($mItem, "[$m]header"); ?>
</div>
<?php endforeach; ?>
验证工作符合预期。
如果您想使用AJAX验证,请在控制器中使用CActiveForm::validateTabular()
(而不是普通的validate()
)。
希望这能帮助其他Yii初学者!:)