ZF2 Проверка дубликатов форм по составному ключу

#validation #zend-framework2 #db2 #duplicates #zend-form2

#проверка #zend-framework2 #db2 #дубликаты #zend-form2

Вопрос:

У меня есть форма, имеющая первичный ключ для двух полей (gid, bid). Мне нужно добавить проверку, чтобы блокировать повторяющиеся записи в базе данных.

Для этого я проверил решение ZF2 . http://framework.zend.com/manual/2.2/en/modules/zend.validator.db.html#excluding-records . Хотя такой подход к обработке составных ключей выглядит не идеально, но все же я пробую его, потому что он выглядит как единственный встроенный способ. Теперь мне требуется указать значение второго поля (параметр value в exclude), что снова является проблемой. Поскольку я пытаюсь это

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

Как мне получить это значение, поскольку форма является абсолютно отдельным классом / файлом, чем контроллер, где у меня есть отправленные значения формы. Существует ли какое-то лучшее архитектурное решение этой проблемы или единственным решением является какой-то взлом для передачи отправленного значения поля в класс Form?

Примечание: я не сторонник создания моего плагина проверки для этой задачи, поскольку короткое время ограничивает функциональность.

Комментарии:

1. откуда берется bid значение?

2. @ins0, на самом деле это мой вопрос.

Ответ №1:

Вы можете выполнить всю работу в своей форме. Для достижения этого вы могли бы определить свои формы как фабрики в вашем модуле Module.php.

Module.php

 use MyNamespaceMyForm;

//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 ZendFormForm;

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

Таким образом, вы можете воспользоваться Service Manager в своей форме. И общедоступный, postData где вы найдете bid значение, которое ищете для построения своего NoRecordExists фильтра.

Комментарии:

1. Решение работает, и проверка выполняется нормально. Только одна проблема, только в одном поле отображаются ошибки, но, как я полагаю, это нелегко исправить.

2. exclude Вариантом является смешанный параметр. Это означает, что это может быть массив с ключами field и value или строка типа gid = [?] AND bid = [?] . Вы могли бы попробовать это таким образом и добавить этот exclude параметр к средствам проверки обоих входных данных.

Ответ №2:

Вы могли бы добавить параметры в getInputFilter, например, так :

 getInputFilter($gid, $bid)
  

И затем на контроллере, когда вы устанавливаете фильтр, вы передаете 2 параметра, а затем просто проверяете как $form->isValid(); …

Альтернативный вариант попробуйте это:

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

Комментарии:

1. Я не знаю, в чем проблема, но проверка не выполняется, И база данных каждый раз выдает повторяющуюся ошибку. Я проверил через bjyprofile и обнаружил, что запрос выполнен правильно, но ошибка проверки не выдается.

2. можете ли вы опубликовать код настройки фильтра на контроллере?

3. $postData = $request->getPost();$form->setInputFilter($form->getInputFilter($postData['bid']));

4. Хорошо, поэтому попробуйте использовать другое имя для вашего метода, назовите его getInputFilterCustom($bid, …); потому что теперь вы переопределяете текущий getInputFilter в форме, а это всегда приводит к нежелательному поведению.

5. Но все остальные проверки просто отлично работают. У меня есть только одно изменение из руководства, в котором я помещаю inputfilter в саму форму вместо Model.

Ответ №3:

Я не уверен в вашем варианте использования. Если бы вы добавили запись в базе данных, первичные ключи для этой таблицы не были бы известны до тех пор, пока вы все равно не вставите — если у вас есть ограничения на внешний ключ, вы могли бы обработать исключение из базы данных.

Я не сторонник создания моего плагина проверки для этой задачи

Средство проверки также не предназначено для проверки нескольких полей, поскольку они прикреплены к элементу формы по принципу 1-1. Поэтому вам нужно будет создать свою собственную.

Приведенный ниже пример НЕ тестировался, поэтому воспринимайте его скорее как пример подхода, чем как рабочий код.

Бит ключа — это isValid метод.

 namespace MyModuleValidatorDb;

use ZendValidatorDbNoRecordExists;

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]) amp;amp; 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' => 'MyModuleValidatorDbCompositeNoRecordExists',
    'options' => array (
        'table'    => 'gtable',
        'field'    => 'gid',
        'field2'   => 'bid',
        'adapter'  => $this->dbAdapter,
        'messages' => array(
            ZendValidatorDbNoRecordExists::ERROR_RECORD_FOUND => 'The specified key already exists in database' 
        ),
    )
),