我有一个字段集,用于“添加”和“编辑”表单。
字段集实现InputFilterProviderInterface
以提供其验证。
在验证添加操作时,我需要检查数据库中是否存在相同值的数据库记录,因此我使用NoRecordExists
验证器。
到目前为止一切顺利。但是,当我在编辑表单中使用相同的字段集时,验证将失败,因为显然已经有一个具有特定值的记录,其正在编辑的记录。
所以我转向exclude
验证器的NoRecordExists
选项,并使用我正在编辑的记录的'id'(这是我的主键字段)排除记录。
所以我几乎就在那里,唯一不能解决的是如何获取我想在getInputFilterSpecification
中创建inputfilter时要排除的'id'值。
这是我的字段集代码。如果有人可以告诉我如何从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,
);
}
答案 0 :(得分: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);
}
}
答案 1 :(得分:0)
在这种情况下使用此验证器是错误的。
会发生什么:验证器向数据库发送SELECT查询。如果找到了某些内容,则会报告“无效”。
如果找不到某些内容,它会报告“有效”,但如果第二个请求同时执行相同操作并返回“有效”,该怎么办?谁赢?如何处理一个查询的失败,因为显然你想将输入写入数据库?
这会导致TOCTOU问题。只有在尝试插入新记录并等待数据库抱怨非唯一索引违规时,才能将唯一记录写入数据库。这是写操作的预期结果,可以处理。
验证器并非无用:您仍然可以使用它来检查数据库中是否存在某些问题,例如:在用户正在填写用户名等的Ajax查询中。检查并从数据库获取布尔值仅供读取完全没问题。但作为输入验证器,这是错误的。
答案 2 :(得分: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'
),
),
),
),
)));
答案 3 :(得分: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 );
}
}
希望这有帮助
答案 4 :(得分:0)
在这里,我找到了添加操作和编辑操作
的解决方案<强> 控制器: 强>
在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'
),
),
),
),
));
}
}