ZF2 +复合键上的重复表单验证

时间:2014-07-07 09:14:54

标签: validation zend-framework2 db2 duplicates zend-form2

我有一个表格在两个字段(gid,bid)上有主键。我需要添加验证来阻止重复的条目进入数据库。

我已经使用ZF2解决方案进行了检查。 http://framework.zend.com/manual/2.2/en/modules/zend.validator.db.html#excluding-records。虽然这种处理复合键的方法看起来不是理想的方式,但我仍然在尝试它,因为它看起来只是建立方式。现在它需要我提供第二个字段的值(排除中的值选项),这也是一个问题。正如我正在尝试的那样

$inputFilter->add(array(
     'name'     => 'gid',
     'required' => true,
     'validators' => array(
         array(
                'name' => 'NotEmpty',
                'options' => array(
                    'messages' => array(
                        'isEmpty' => 'required'
                    ),
                 ),
         ),
         array (
            'name' => 'Zend\Validator\Db\NoRecordExists',
            'options' => array (
                'table' => 'gtable',
                'field' => 'gid',
                'adapter' => $this->dbAdapter,
                'messages' => array(
                    \Zend\Validator\Db\NoRecordExists::ERROR_RECORD_FOUND => 'The specified key already exists in database' 
                ),
                'exclude' => array(
                    'field' => 'bid',
                    'value' => [?],
                ),
            )
        ),
     )
 ));

如何获取此值,因为Form是绝对单独的Class / File而不是我拥有提交的表单值的控制器。是否有一些更好的架构解决方案存在或者有些黑客将提交的字段值传递给Form Class只是解决方案?

注意:我不赞成为此任务构建我的验证插件,因为短时间限制了功能。

3 个答案:

答案 0 :(得分:1)

您可以在表单中完成所有工作。为此,您可以在模块 Module.php 中将表单定义为工厂

<强> Module.php

use MyNamespace\MyForm;

//NOTE THAT THE SERVICE MANAGER IS INJECTED. YOUR FORM COULD RECEIVE IT THROUGH THE CONSTRUCTOR
public function getServiceConfig()
{
    return array(
        'factories' => array(
            'my_form' => function( $sm ) {
                $form = new MyForm( $sm );

                return $form;
            },
        ),
    );
}

当您想要使用表单时,只需在控制器中使用此代码即可:

class MyController extends AbstractActionController
{
    public function createAction() {
        $form = $this->getServiceLocator()->get( 'my_form' ) );
        (...)
    }
}

你的 MyForm.php

use Zend\Form\Form;

class MyForm extends Form
{
    public $serviceManager, $request, $postData;

    public function __construct( $serviceManager ) {
        parent::__construct( null );

        $this->serviceManager = $serviceManager;
        $this->request = $serviceManager->get( 'Application')->getMvcEvent()->getRequest();
        $this->postData = get_object_vars( $this->request->getPost() );
    }
}

通过这种方式,您可以在表单中获得服务管理器的优势。以及公众postData,您可以在其中找到构建bid过滤器所需的NoRecordExists值。

答案 1 :(得分:0)

您可以将参数添加到getInputFilter,如下所示:

getInputFilter($gid, $bid)

然后在控制器上,当您设置过滤器时,您传递了2个参数,然后检查为$ form-&gt; isValid(); ...

替代方案请尝试:

                array(
                    'name' => 'Db\NoRecordExists',
                    'options' => array(
                        'table' => 'gtable',
                        'field' => 'gid',
                        'adapter' => $this->dbAdapter,
                    ),
                ),

答案 2 :(得分:0)

我不确定你的用例。如果要添加数据库条目,则无论如何插入都不会知道该表的主键 - 如果您有外键约束,则可以处理数据库中的异常。

  

我不赞成为此任务构建我的验证插件

验证器也不是为了验证多个字段而设计的,因为它们以1-1为基础附加到表单元素。因此,您需要创建自己的。

以下示例已对 NOT 进行了测试,因此请将其作为方法的示例而不是工作代码。

关键位是isValid方法。

namespace MyModule\Validator\Db;

use Zend\Validator\Db\NoRecordExists;

class CompositeNoRecordExists extends NoRecordExists
{
    protected $field2;

    protected $field2Value;

    public function __construct($options = null)
    {
        parent::__construct($options);

        if (array_key_exists('field2', $options)) {

            $this->setField2($options['field2']);
        } else {
            throw new \BadMethodCallException('Missing field2 option!');
        }
    }

    protected function setField2Value(array $context)
    {
        if (! isset($context[$this->field2])) {

            throw new \BadMethodCallException('Unable to find value for field 2');
        }
        $this->field2Value = $context[$this->field2];
    }

    public function isValid($value)
    {
        // The isValid() method is actually given a 2nd argument called $context
        // Which is injected by the inputFilter, via the input and into the validator chain
        // $context contains all of RAW form element values, keyed by thier element name.

        // Unfortunately due to the ValidatorInterface you are unable to add this to the method
        // signature. So you will need to be 'creative':

        $args = func_get_args();

        if (isset($args[1]) && is_array($args[1])) {

            $this->setField2Value($args[1]);

        } else {
            throw new \BadMethodCallException('Missing validator context');
        } 

        return parent::isValid($value);
    }


    public function getSelect()
    {
        $select = parent::getSelect();

        $select->where->equalTo($this->field2, $this->field2Value);

        return $select;
    }

}

然后您需要做的就是更新验证器配置,添加field2字段名称。

array (
    'name' => 'MyModule\Validator\Db\CompositeNoRecordExists',
    'options' => array (
        'table'    => 'gtable',
        'field'    => 'gid',
        'field2'   => 'bid',
        'adapter'  => $this->dbAdapter,
        'messages' => array(
            \Zend\Validator\Db\NoRecordExists::ERROR_RECORD_FOUND => 'The specified key already exists in database' 
        ),
    )
),