我正在通过为自己使用构建一些实用网站来逐渐建立我的Zend技能。我一直在使用Zend Forms和表单验证,到目前为止,我很高兴我能理解Zend的做事方式。然而,我对如何在编辑表单和映射到数据库列的字段上使用Zend_Validate_Db_NoRecordExists()有点困惑,因为该列必须是唯一的。
例如,使用这个简单的表:
TABLE Test
(
ID INT AUTO_INCREMENT,
Data INT UNIQUE
);
如果我只是在Table Test中添加新行,我可以将验证器添加到Data字段的Zend表单元素中,如下所示:
$data = new Zend_Form_Element_Text('Data');
$data->addValidator( new Zend_Validate_Db_NoRecordExists('Test', 'Data') )
在表单验证时,此验证器将检查Data元素的内容是否已经存在于表中。因此,插入到Test中可以进行而不违反Data字段唯一性的限定条件。
但是,当编辑Test表中现有的行时情况就不同了。在这种情况下,验证器需要检查元素值是否符合两个互斥条件之一:
用户更改了元素值,新值目前不存在于表中。
用户未更改元素值。因此,该值当前存在于表中(这是可以接受的)。
Zend Validation Docs、谈到了向NoRecordExists()验证器添加参数以排除验证过程中的记录。其想法是“验证表以查找任何匹配的行,但忽略具有此特定值的字段的任何匹配项”。这种用例是在编辑表时验证元素所需的。在1.9中执行此操作的伪代码如下(实际上我是从1.9源代码中获取的 - 我认为当前的文档可能是错误的):
$data = new Zend_Form_Element_Text('Data');
$data->addValidator( new Zend_Validate_Db_NoRecordExists('Test', 'Data',
array ('field'=>'Data', 'Value'=> $Value) );
问题在于需要排除的值($Value)在验证器实例化时就已经绑定了(也就是在表单实例化时)。但是,当表单正在编辑记录时,该值需要绑定到表单最初填充数据时$ data字段的内容 - 即最初从Test表行中读取的Data值。但是,在典型的Zend模式中,表单被实例化和填充分为两个步骤,这使得将排除值绑定到所需元素值变得困难。以下Zend伪代码标记了我希望将$Value绑定到NoRecordExists()验证器的位置(请注意,这是一种常见的Zend控制器模式):
$form = new Form()
if (is Post) {
$formData = GetPostData()
if ($form->isValid($formData)) {
Update Table with $formData
Redirect out of here
} else {
$form->populate($formData)
}
} else {
$RowData = Get Data from Table
$form->populate($RowData) <=== This is where I want ('value' => $Value) bound
}
我可以创建一个Zend_Form子类并重写populate()方法,在初始表单填充时进行一次性插入NoRecordExists()验证器,但这对我来说似乎是一个巨大的hack。所以我想知道其他人的想法,是否已经有已解决此问题的模式写下?
编辑2009-02-04
我在考虑这个问题的唯一合理解决方案是编写自定义验证器并忘记Zend版本。我的表单具有记录ID作为隐藏字段,因此鉴于表和列名称,我可以制定一些SQL来测试唯一性并排除指定ID的行。当然,这让我开始思考如何将表单与模型应该隐藏的数据库层连接起来!