Zend_Validate_Db_RecordExists排除逻辑问题

时间:2013-02-05 11:38:21

标签: php zend-framework zend-validate

我有一个求职申请表,管理员需要从某些用户ID列表中选择。该列表仅包含“雇主”类型的用户ID,但是我想验证管理员输入,因此如果他手动插入不存在的id或者不是“雇主”类型的用户,则验证应该失败。我认为执行此操作的代码将是:

new Zend_Validate_Db_RecordExists(
    array(
        'table' => 'users',
        'field' => 'id',
        'exclude' => "mode != 'employer'"
    )
)

所以,我正在搜索表用户中的所有记录,不包括模式!='雇主'的那些记录 - 如果存在这样的记录,其中id等于从输入中选择的记录,则它通过验证。但是,上面的代码不起作用 - 我必须做'exclude' => "mode = 'employer'",所以排除实际上等于where语句。我对这里逻辑的理解是错误的 - 有人可以告诉我为什么吗?

PHP:5.2.17,Zend:1.10.4

编辑:(对@ro ko查询的评论,因为它可能清除了一些东西)

请在此处找到表格和示例代码:http://pastebin.com/C7AXMNTZ。根据我的理解,这应该对Joker(是雇主)有效,但对于Kingpin(不是雇主)和Poison Ivy(不在数据库中)是假的 - 因为你可以看到结果不是我所期待的。

4 个答案:

答案 0 :(得分:2)

A)'exclude' => "mode != 'employer'"

    $id = new Zend_Form_Element_Select("id");
    $id->setRegisterInArrayValidator(false);
    $id->addValidator(new Zend_Validate_Db_RecordExists(array(
        'table' => 'villains',
        'field' => 'id',
        'exclude' => "mode != 'employer'"
    )));

生成以下查询:

SELECT `villains`.`id` 
    FROM `villains` 
    WHERE (`id` = :value) AND (mode != 'employer') 
    LIMIT 1

B)'exclude' => "mode = 'employer'"

    $id = new Zend_Form_Element_Select("id");
    $id->setRegisterInArrayValidator(false);
    $id->addValidator(new Zend_Validate_Db_RecordExists(array(
        'table' => 'villains',
        'field' => 'id',
        'exclude' => "mode = 'employer'"
    )));

生成以下查询:

SELECT `villains`.`id` 
    FROM `villains` 
    WHERE (`id` = :value) AND (mode = 'employer') 
    LIMIT 1

C)'exclude' => array("field" => "mode", "value" => "employer")

    $id = new Zend_Form_Element_Select("id");
    $id->setRegisterInArrayValidator(false);
    $id->addValidator(new Zend_Validate_Db_RecordExists(array(
        'table' => 'villains',
        'field' => 'id',
        'exclude' => array(
            "field" => "mode",
            "value" => "employer"
        )
    )));

生成以下查询:

SELECT `villains`.`id` 
    FROM `villains` 
    WHERE (`id` = :value) AND (`mode` != 'employer') 
    LIMIT 1

您想要 B 令人困惑,可以说组件的逻辑和行为是倒退的。尽管如此,您想要的行为来自示例B。

附录

我们可以通过破解测试(我真的意味着共同攻击)来检查上述内容是否按预期工作。

test1和test2都通过,但正如您从供应商那里看到的,它们都会产生不同的结果。

class SO14706653Test extends PHPUnit_Framework_TestCase
{
    /**
     * @var Zend_Test_PHPUnit_Db_Connection
     */
    public $dbConnection;

    public function getRowCount($tableName) {
        $query = "SELECT COUNT(*) FROM ".$this->dbConnection->quoteSchemaObject($tableName);
        return (int) $this->dbConnection->getConnection()->query($query)->fetchColumn();
    }

    // hack a very quick setup for tests
    public function setup() {
        $app = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini');
        $app->bootstrap();
        $dbAdapter = $app->getBootstrap()->getResource('db'); /* @var $db Zend_Db_Adapter_Pdo_Mysql */
        $this->dbConnection = new Zend_Test_PHPUnit_Db_Connection($dbAdapter, 'unittests');

        $dbAdapter->exec("CREATE TABLE IF NOT EXISTS `villains` (
              `id` int(11) NOT NULL AUTO_INCREMENT,
              `name` varchar(255) NOT NULL,
              `mode` varchar(255) NOT NULL,
              PRIMARY KEY (`id`)
            ) ENGINE=InnoDB  DEFAULT CHARSET=latin1");

        $dbAdapter->exec('DELETE FROM villains'); // clean out db data

        $dbAdapter->exec("
            INSERT INTO `villains` VALUES(1, 'Joker', 'employer');
            INSERT INTO `villains` VALUES(2, 'Kingpin', '');
            INSERT INTO `villains` VALUES(3, 'Penguin', '');
        ");
    }

    // ensure the above setup is working as expected
    public function assertPreConditions() {
        $this->assertEquals(3, $this->getRowCount('villains')); 
    }

    public function provideTest1()
    {
        return [
            // form data        is valid?       isRequired?
            [['id' => '1'],     false,          false],
            [['id' => '2'],     true,           false],
            [['id' => '3'],     true,           false],
            [['id' => ''],      true,           false],
            [[],                true,           false],
            [['id' => '856'],   false,          false],
            [['id' => '856'],   false,          true],
            [['id' => ''],      false,          true],
            [[],                false,          true],
        ];
    }

    public function provideTest2()
    {
        return [
            //  form data       is valid?       isRequired?
            [['id' => '1'],     true,           false],
            [['id' => '2'],     false,          false],
            [['id' => '3'],     false,          false],
            [['id' => ''],      true,           false],
            [[],                true,           false],
            [['id' => '856'],   false,          false],
            [['id' => '856'],   false,          true],
            [['id' => ''],      false,          true],
            [[],                false,          true],
        ];
    }

    /**
     * @dataProvider provideTest1
     */
    public function test1(array $data, $isValid, $isRequired)
    {
        $form = new Zend_Form();
        $id = new Zend_Form_Element_Select("id");
        $id->setRegisterInArrayValidator(false);
        $id->addValidator(new Zend_Validate_Db_RecordExists(array(
            'table' => 'villains',
            'field' => 'id',
            'exclude' => "mode != 'employer'"
        )));
        $id->setRequired($isRequired);
        $form->addElement($id);

        // produces the query
        // SELECT `villains`.`id`
        // FROM `villains`
        // WHERE (`id` = :value) AND (mode != 'employer')
        // LIMIT 1

        $this->assertSame($isValid, $form->isValid($data));
    }

    /**
     * @dataProvider provideTest2
     */
    public function test2(array $data, $isValid, $isRequired)
    {
        $form = new Zend_Form();
        $id = new Zend_Form_Element_Select("id");
        $id->setRegisterInArrayValidator(false);
        $id->addValidator(new Zend_Validate_Db_RecordExists(array(
            'table' => 'villains',
            'field' => 'id',
            'exclude' => "mode = 'employer'"
        )));
        $id->setRequired($isRequired);
        $form->addElement($id);

        // produces the query
        // SELECT `villains`.`id` 
        // FROM `villains` 
        // WHERE (`id` = :value) AND (mode = 'employer') 
        // LIMIT 1

        $this->assertSame($isValid, $form->isValid($data));
    }
}

答案 1 :(得分:0)

您在exclude选项中传递数组。例如

$this->getElement('username')->addValidator(
    new Zend_Validate_Db_NoRecordExists(
        array(
        'table' => 'user', 
        'field' => 'username', 
        'exclude' => array(
                'field' => 'username', 
                'value' => $username
            )
        )
    )
);

Check here for more info.

编辑:再次阅读问题后,我发现我实际上没有回答。我将给出的唯一其他建议是使用quoteInto语句,如文档中那样。

$email     = 'user@example.com';
$clause    = $db->quoteInto('email != ?', $email);
$validator = new Zend_Validate_Db_RecordExists(
    array(
        'table'   => 'users',
        'field'   => 'username',
        'exclude' => $clause
    )
);

此处还有一个关于Zend_Validate_Db_RecordExists的另一个问题的链接,该答案非常好。

How to modify zend Db_RecordExists validator where clause?

答案 2 :(得分:0)

我觉得你在这里感到困惑。验证器的逻辑很好。

  

如何?
  那么排除在这里会增加对你的病情的否定。

'exclude' => "mode = 'employer'"

OR

'exclude' => array(
                'field' => 'mode', 
                'value' => 'employer'
            )

被解释为验证者应该对mode = employer以外的条件有效。

编辑:如果您看到In the manual,您会看到一个说明:“上面的示例将检查表格,以确保除了id = $ user_id包含的记录之外没有其他记录值$ username。“这个陈述应该为你清楚我认为的事情。

答案 3 :(得分:0)

根据文档(搜索The above example will check the table to ensure no records other than the one where id = $user_id contains the value $username.),exclude选项确实可用作WHERE语句。

我只能说,使用此类关键字获取此功能的任何人都是一个好主意但有一种邪恶的幽默感。