Php NoRecordExists输入过滤器

Php NoRecordExists输入过滤器,php,zend-framework2,Php,Zend Framework2,我有一个字段集,用于“添加”和“编辑”表单 字段集实现InputFilterProviderInterface以提供其验证 在验证添加操作时,我需要检查数据库中不存在具有相同值的数据库记录,因此我使用NoRecordExists验证器 到目前为止一切都很好。但是,当我在编辑表单中使用相同的字段集时,验证将失败,因为显然已经有一条记录具有特定的值,它就是正在编辑的记录 因此,我转到NoRecordExists验证器的exclude选项,并用我正在编辑的记录的“id”(这是我的主键字段)排除该记录

我有一个字段集,用于“添加”和“编辑”表单

字段集实现
InputFilterProviderInterface
以提供其验证

在验证添加操作时,我需要检查数据库中不存在具有相同值的数据库记录,因此我使用
NoRecordExists
验证器

到目前为止一切都很好。但是,当我在编辑表单中使用相同的字段集时,验证将失败,因为显然已经有一条记录具有特定的值,它就是正在编辑的记录

因此,我转到
NoRecordExists
验证器的
exclude
选项,并用我正在编辑的记录的“id”(这是我的主键字段)排除该记录

所以我就快到了,我唯一不能解决的问题是如何在
getInputFilterSpecification
中创建inputfilter时获取要排除的“id”值

这是我的字段集代码。如果有人能告诉我如何从
getInputFilterSpecification
中访问表单(或绑定对象)的其他属性,我将不胜感激

也许我需要以不同的方式实现我的imputfilter来实现这一点?或者甚至实现一个自定义验证器?但对于一个看起来相当常规的用例来说,定制验证器无疑是过火了

非常感谢。 :wq


在InputFilter之外检查该条件会更简单

如果这样做,则可以对更新和插入使用相同的表单

您可以a)使用一个单独的操作来更新和插入(CRUD),或者b)如果您想让它们在条件下更新/插入,请执行以下操作

// form validates for update or insert now...

if($form->isValid()) {
    if($mapper->exists($object)) {
        $mapper->update($object);
    }
    else {
        $mapper->save($object);
    }
}

在这种情况下使用此验证器是错误的

发生的情况:验证器向数据库发送一个SELECT查询。如果它发现了什么,它将报告“无效”

如果它没有找到某个内容,它将报告“valid”,但如果第二个请求同时执行相同操作并返回“valid”,该怎么办。谁赢了?一个查询的失败是如何处理的,因为您显然想将输入写入数据库

这就是所谓的问题。只有尝试插入新记录并等待数据库抱怨非唯一索引冲突,才能将唯一记录写入数据库。这是写入操作的预期结果,可以处理


验证器不是无用的:您仍然可以使用它来检查数据库中是否有某种东西,例如,当用户正在填写用户名等时,在Ajax查询中。检查并从数据库中获取布尔值仅用于读取完全可以。但作为输入验证器,这是错误的。

这是我的工作解决方案:

$inputFilter->add($factory->createInput(array(
            'name'     => 'role_name',
            'required' => true,
            'filters'  => array(
                array('name' => 'StripTags')
            ),
            'validators' => array(
                array(
                    'name'    => 'StringLength',
                    'options' => array(
                        'encoding' => 'UTF-8',
                        'min'      => 2,
                        'max'     => 15
                    ),
                ),
                array(
                    'name'    => 'Zend\Validator\Db\NoRecordExists',
                    'options' => array(
                        'table' => 'user_role',
                        'field' => 'code',
                        'adapter' => \Zend\Db\TableGateway\Feature\GlobalAdapterFeature::getStaticAdapter(),
                        'messages' => array(
                            \Zend\Validator\Db\NoRecordExists::ERROR_RECORD_FOUND => 'The specified name already exists in database' 
                        ),
                    ),
                ),
            ),
        )));

我已经找到了解决这个问题的办法。 基本上,默认的NoRecordExists验证器期望在配置参数中排除值和列。这可以在Ritesh提到的控制器中更改;我玩了一会儿,得到了这个解决方案

我使用的是isValid函数中提供的上下文数组变量。您不发送id值,而是发送表单字段的值以从中提取

在InputFilter中,您有以下内容

$this->add ( array (
    'name'  => 'user_email',
    'required' => true,
    'filters' => array (
        array ( 
            'name' => 'StringTrim',
        ),
        array ( 
            'name' => 'StripTags',
        ),
    ),
    'validators' => array (
        array (
            'name' => 'EmailAddress',
            'options' => array (
                'domain' => true,
            )
        ),
        array (
            'name' => 'Application\Validator\NoRecordExists',
            'options' => array (
                'table' => 'user',
                'field' => 'user_email',
                'adapter' => \Zend\Db\TableGateway\Feature\GlobalAdapterFeature::getStaticAdapter( ),
                'exclude' => array(
                    'field' => 'user_id',
                    'formvalue' => 'user_id',
                ),
            )
        ),

    )
) );
表单中定义了一个隐藏元素user_id;在那里设置的值用于内部检查

<?php

namespace Application\Validator;

class NoRecordExists extends \Zend\Validator\Db\NoRecordExists {

    public function isValid( $value, $context=array( ) ) {

        $exclude = $this->getExclude( );

        if( is_array( $exclude ) ){
            if ( array_key_exists( 'formvalue', $exclude ) ) {
                $formvalue = $exclude[ 'formvalue' ];
                $exclude[ 'value' ] = $context[ $formvalue ];
                $this->setExclude( $exclude );
            }
        }

        return parent::isValid( $value );

    }

}

在这里,我找到了添加操作和编辑操作的解决方案

控制器:

addAction:

            $postData = $this->request->getPost ();
            $dbAdapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter');
            $form->setInputFilter(new FormFilter($dbAdapter));
            $form->setData ($postData);
            if (!$form->isValid ()) {
                $viewModel->error = true;
                return $viewModel;
            } 
            $post = $request->getPost();
            $dbAdapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter');                
            $form->setInputFilter(new FormFilter($dbAdapter,$Id));
            $form->setData ($post);
            $Id = $post['id'];
            if (!$form->isValid ()) {
                    $viewModel->error = true;
                    $viewModel->Id = $Id;
                    $viewModel->form = $form;
                    return $viewModel;
            }
在编辑操作中:

            $postData = $this->request->getPost ();
            $dbAdapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter');
            $form->setInputFilter(new FormFilter($dbAdapter));
            $form->setData ($postData);
            if (!$form->isValid ()) {
                $viewModel->error = true;
                return $viewModel;
            } 
            $post = $request->getPost();
            $dbAdapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter');                
            $form->setInputFilter(new FormFilter($dbAdapter,$Id));
            $form->setData ($post);
            $Id = $post['id'];
            if (!$form->isValid ()) {
                    $viewModel->error = true;
                    $viewModel->Id = $Id;
                    $viewModel->form = $form;
                    return $viewModel;
            }
表单过滤器文件中:

class FormFilter extends InputFilter {

    public function __construct ($dbAdapter, $id = '')
    {
        $this->dbAdapter = $dbAdapter;
        $this->add(array(
                'name'       => 'name',
                'required'   => true,
                'filters'  => array(
                        array('name' => 'StripTags'),
                        array('name' => 'StringTrim'),
                ),
                'validators' => array(
                        array(
                                'name'    => 'StringLength',
                                'options' => array(
                                        'encoding' => 'UTF-8'
                                ),
                        ),
                        array(
                                'name'    => 'Zend\Validator\Db\NoRecordExists',
                                'options' => array(
                                        'table' => 'test',
                                        'field' => 'name',
                                        'adapter' => $this->dbAdapter,
                                        'exclude' => array(
                                                'field' => 'id',
                                                'value' => $id,
                                        ),
                                        'messages' => array(
                                                \Zend\Validator\Db\NoRecordExists::ERROR_RECORD_FOUND => 'The specified name already exists in database'
                                        ),
                                ),
                        ),
                ),
        ));
    }
}   

表单发送后,您可以从控制器访问验证程序,然后根据需要修改验证程序;)谢谢Sam,我会在下面看看这个和Andrew的建议。Andrew,谢谢你的评论。我实际上是在使用条令来管理我的实体,所以CRUD插入/更新对我来说很重要,只需保持现有的或新的实体即可。我将在表单验证之外验证副本。此外,我将更新我的原始帖子,以包含我的“编辑”控制器操作。