2

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

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

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

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

所以我转向验证器的exclude选项,NoRecordExists并使用我正在编辑的记录的“id”(这是我的主键字段)排除记录。

所以我快到了,唯一我无法解决的是如何获得我想在创建输入过滤器时排除的“id”值getInputFilterSpecification

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

也许我需要以不同的方式实现我的 imputfilter 来做到这一点?甚至实现自定义验证器?但是对于看起来非常常规的用例来说,自定义验证器肯定会过大...

提前谢谢了。:wq

<?php
namespace Kickoff\Form\Competition;

use Kickoff\Form\AbstractFieldset,
    Kickoff\Model\Entities\Competition,
    DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator,
    Zend\InputFilter\InputFilterProviderInterface;


class CompetitionFieldset extends AbstractFieldset implements InputFilterProviderInterface
{
    public function init()
    {
        $this->setName('Competition')
            ->setHydrator(new DoctrineHydrator($this->getObjectManager(),'Kickoff\Model\Entities\Competition'))
            ->setObject(new Competition())
            ->setLabel('Competition')
            ->setAttribute('class','form-collection');

        $this->add(array(
            'type' => 'Zend\Form\Element\Hidden',
            'name' => 'id',
        ));

        $this->add(array(
            'name' => 'name',
            'options' => array(
                'label' => 'Competition name',
                'admin_inline' => true,
            ),
        ));

        $this->add(array(
            'name' => 'long_name',
            'options' => array(
                'label' => 'Competition long name',
                'admin_inline' => true,
            ),
            'attributes' => array(
                'class' => 'input-xxlarge',
            ),
        ));

        $this->add(array(
            'type' => 'Zend\Form\Element\Collection',
            'name' => 'leagues',
            'options' => array(
                'label' => 'Leagues',
                'count' => 0,
                'should_create_template' => true,
                'allow_add' => true,
                'target_element' => array(
                    'type' => 'LeagueFieldset',
                ),
            ),
        ));
    }


    /**
     * Implement InputFilterProviderInterface
     */
    public function getInputFilterSpecification()
    {
        return array(
            'name' => array(
                'filters'  => array(
                    array('name' => 'Zend\Filter\StringTrim'),
                ),
                'validators' => array(
                    'notempty' => array(
                        'name' => 'NotEmpty',
                        'break_chain_on_failure' => true,
                        'options' => array(
                            'messages' => array('isEmpty' => 'Competition name is required.',),
                        ),
                    ),
                    'length' => array(
                        'name' => 'StringLength',
                        'options' => array(
                            'max' => '64',
                            'messages' => array(
                                'stringLengthTooLong' => 'Competition name must be no more than 64 characters.',
                            ),
                        ),
                    ),
                    'unique' => array(
                        'name' => 'Db\NoRecordExists',
                        'options' => array(
                            'table' => 'competition',
                            'field' => 'name',
                            'adapter' => $this->serviceManager->getServiceLocator()->get('db'),
                            'exclude' => array(
                                'field' => 'id',
                                'value' => '',
                            ),
                            'messages' => array(
                                'recordFound' => 'A competition already exists with this name',
                            ),
                        ),
                    ),
                ),
            ),
            'long_name' => array(
                'filters'  => array(
                    array('name' => 'Zend\Filter\StringTrim'),
                ),
                'validators' => array(
                    'length' => array(
                        'name' => 'StringLength',
                        'options' => array(
                            'max' => '128',
                            'messages' => array(
                                'stringLengthTooLong' => 'Competition long name must be no more than 128 characters.',
                            ),
                        ),
                    ),
                ),
            ),
        );
    }
}

编辑:将我的“编辑”控制器操作添加到这篇文章中:

public function editCompetitionAction()
{
    $id = $this->params()->fromRoute('competition_id');

    $repository = $this->getEntityManager()->getRepository('Kickoff\Model\Entities\Competition');
    $competition = $repository->find($id);

    if (null == $competition) {
        $this->getResponse()->setStatusCode(404);
        return;
    }

    $formManager = $this->serviceLocator->get('FormElementManager');
    $form = $formManager->get('Kickoff\Form\Competition\CompetitionForm');
    $form->bind($competition);

    $request = $this->getRequest();
    if ($request->isPost()) {
        $form->setData($request->getPost());
        $this->logger->debug("Validator is ".print_r($form->getValidator(),1));
        if ($form->isValid()) {
            $this->getEntityManager()->persist($competition);
            $this->getEntityManager()->flush();
        }
    }

    return array(
        'form' => $form,
    );
}
4

5 回答 5

1

在 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);
    }
}
于 2013-04-11T22:09:03.100 回答
1

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

控制器:

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;
            } 

editAction:

            $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'
                                        ),
                                ),
                        ),
                ),
        ));
    }
}   
于 2016-02-23T12:26:56.790 回答
0

这是我的工作解决方案:

$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' 
                        ),
                    ),
                ),
            ),
        )));
于 2013-11-01T14:36:35.173 回答
0

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

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

如果它没有找到某些东西,它会报告“有效”,但如果同时第二个请求做同样的事情并返回一个“有效”怎么办。谁赢?以及如何处理一个查询的失败,因为显然您想将输入写入数据库?

这称为TOCTOU问题。将唯一记录写入数据库只能通过尝试插入新记录并等待数据库抱怨非唯一索引违规来完成。这是写操作的预期结果,可以处理。

验证器并非没用:您仍然可以使用它来检查数据库中是否存在问题,例如,在用户填写用户名的过程中的 Ajax 查询中等。检查并从中获取布尔值仅用于读取目的的数据库完全可以。但作为输入验证器,这是错误的。

于 2013-04-12T18:37:09.300 回答
0

我找到了解决这个问题的方法。基本上,默认的 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 );

    }

}

希望这可以帮助

于 2014-06-17T12:23:12.240 回答