我正在通过构建一些实用程序网站供我自己使用来慢慢提高我的 Zend 技能。我一直在使用 Zend 表单和表单验证,到目前为止,我很高兴我已经理解了 Zend 的做事方式。但是,我对如何在编辑表单和映射到必须唯一的数据库列的字段的上下文中使用 Zend_Validate_Db_NoRecordExists() 有点困惑。
例如使用这个简单的表
TABLE Test
(
ID INT AUTO_INCREMENT,
Data INT UNIQUE
);
如果我只是向 Table Test 添加一个新行,我可以为 Data 字段的 Zend Form 元素添加一个验证器,如下所示:
$data = new Zend_Form_Element_Text('Data');
$data->addValidator( new Zend_Validate_Db_NoRecordExists('Test', 'Data') )
在表单验证时,此验证器将检查 Data 元素的内容是否已存在于表中。因此,插入测试可以继续进行,而不会违反数据字段 UNIQUE 限定符。
但是,在编辑 Test 表的现有行时情况有所不同。在这种情况下,验证器需要检查元素值是否满足两个互斥条件之一:
用户已更改元素值,而新值当前不存在于表中。
用户未更改元素值。因此,该值当前确实存在于表中(这没关系)。
Zend Validation Docs讨论了向 NoRecordExists() 验证器添加一个参数,以便从验证过程中排除记录。这个想法是“验证表以查找任何匹配的行,但忽略 a 字段具有此特定值的任何命中”。这样的用例是在编辑表格时验证元素所需要的。在 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 字段的内容 - 即最初从测试表行读取的数据值。但是在典型的 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() 验证器,但这对我来说似乎是一个巨大的黑客攻击。所以我想知道其他人的想法,是否有一些已经写下来的模式可以解决这个问题?
编辑 2009-02-04
我一直认为解决这个问题的唯一体面的方法是编写一个自定义验证器而忘记 Zend 版本。我的表单将记录 ID 作为隐藏字段,因此给定表名和列名,我可以制作一些 SQL 来测试唯一性并排除具有此类 ID 的行。当然,这让我开始思考如何将表单绑定到模型应该隐藏的 dB 层!