Zend表单编辑和Zend_Validate_Db_NoRecordExists

10

我正在通过为自己使用构建一些实用网站来逐渐建立我的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表中现有的行时情况就不同了。在这种情况下,验证器需要检查元素值是否符合两个互斥条件之一:

  1. 用户更改了元素值,新值目前不存在于表中。

  2. 用户更改元素值。因此,该值当前存在于表中(这是可以接受的)。

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的行。当然,这让我开始思考如何将表单与模型应该隐藏的数据库层连接起来!

7个回答

6
这是实现方法:
  1. 在你的表单中,添加该验证器(例如电子邮件字段):
$email->addValidator('Db_NoRecordExists', true, array('table' => 'user', 'field' => 'email'));
  1. 不要为此添加自定义错误消息,因为在这之后它对我不起作用,例如:

 

$email->getValidator('Db_NoRecordExists')->setMessage('This email is already registered.');

在你的控制器中添加以下代码:
/* Don't check for Db_NoRecordExists if editing the same field */

    $form->getElement('email')
             ->addValidator('Db_NoRecordExists',
                                 false,
                                 array('table' => 'user',
                                       'field' => 'email',
                                       'exclude' => array ('field' => 'id', 'value' => $this->request->get('id'))));

And after this you do verifications, e.g.:

    if ($this->getRequest()->isPost())
            {
                if($form->isValid($this->getRequest()->getPost()))
                {

    ....

就是这样了!


4
这也可以正常工作:
$this->addElement('text', 'email', array(
        'label'      => 'Your email address:',
        'required'   => true,
        'filters'    => array('StringTrim'),
        'validators' => array(
            'EmailAddress',
             array('Db_NoRecordExists', true, array(
                    'table' => 'guestbook', 
                    'field' => 'email',
                    'messages' => array(
                        'recordFound' => 'Email already taken'
                    )
                )
            )
        )
    ));

1
private $_id;

public function setId($id=null)
{
    $this->_id=$id;    
}

public function init()
{
    .....
    if(isset($this->_id)){
        $email->addValidator('Db_NoRecordExists', false, array('table' => 'user', 'field' => 'email','exclude' => array ('field' => 'id', 'value' => $this->_id) )); 
        $email->getValidator('Db_NoRecordExists')->setMessage('This email is already registered.');      
    }

现在你可以使用:

$form = new Form_Test(array('id'=>$id));

1

在审查了压倒性的回应后,我决定使用自定义验证器



0
你可以直接调用 $form->getElement('input')->removeValidator('Zend_Validator_Db_NoRecordExists'); 而不是提供排除。

@The Guy - 这似乎违背了在编辑周期期间确保数据唯一性的目的。我认为这也会使控制器混杂着属于表单的内容。 - Peter M
是的,我同意这有点 hackish。实际上,我对 Zend_Form 的工作方式现在并不是很满意。尽管如此,我仍在使用旧的 ZF 1.6 风格的实现 :P - Thom Wiggers
Guy - 我在去年12月才开始接触1.9版本,所以我还在学习中 - 但是像这样的事情让我感到困惑? - Peter M
注意:删除验证可能会对您的数据库表造成潜在危害。这意味着您可以更新与另一条记录完全相同的记录,而这正是您想要防止的。 - halfpastfour.am

0

我刚刚尝试了这个关于电子邮件地址唯一性的示例,它与以下内容完美配合:

1] 在我的表单中:

// Add an email element
    $this->addElement('text', 'email', array(
        'label'      => 'Email :',
        'required'   => true,
        'filters'    => array('StringTrim'),
        'validators' => array(
            'EmailAddress',
        )
    ));

这里有一些特别的内容,我需要添加它才能让唯一电子邮件地址正常工作:

    $email = new Zend_Form_Element_Text('email');
    $email->addValidator('Db_NoRecordExists', true, array('table' => 'guestbook', 'field' => 'email'));

2] 在我的控制器中:

$form->getElement('email')
     ->addValidator('Db_NoRecordExists',
                         false,
                         array('table' => 'guestbook',
                               'field' => 'email',
                               'exclude' => array ('field' => 'id', 'value' => $request->get('id'))));

if ($this->getRequest()->isPost()) {
        if ($form->isValid($request->getPost())) {

希望这对你们有所帮助!
谢谢。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接