Ajax, дублирование данных в БД

Добрый день всем.

Проблема вот в чем, у меня есть вот такая форма


<div id="overflow">

    <div id="malefactorForm" style="position: fixed; left: 50%; top: 120px; width: 440px; margin-left: -220px; background-color: #6f828b; z-index: 41;">

        <div style="text-align: left;"><span style="color:#fff; position: relative; font-size: 12px; font-weight: bolder; margin-left: 10px; top: 5px;">Форма добавления Нарушителей</span><?php echo CHtml::imageButton('/images/cancel.png', array('onclick' => 'closeForm("malefactorForm")', 'style' => 'margin:5px 10px 0 0', 'align' => 'right'));?></div>

        <div style="margin: 10px; background-color:#fff; padding: 10px;">

            <div class="form">


            <?php $form=$this->beginWidget('CActiveForm', array(

                    'id'=>'malefactor-form',

                    'enableAjaxValidation'=>true,

            )); ?>


                    <p class="note">Поля с  <span class="required">*</span> обязательны.</p>


                    <?php echo $form->errorSummary($model); ?>


                    <div class="row">

                            <?php echo $form->labelEx($model,'FLP'); ?>

                            <?php echo $form->textField($model,'FLP',array('size'=>60,'maxlength'=>512)); ?>

                            <?php echo $form->error($model,'FLP'); ?>

                    </div>


                    <div class="row">

                            <?php echo $form->labelEx($model,'RegistrationPassportAddress'); ?>

                            <?php echo $form->textField($model,'RegistrationPassportAddress',array('size'=>60,'maxlength'=>512)); ?>

                            <?php echo $form->error($model,'RegistrationPassportAddress'); ?>

                    </div>


                    <div class="row buttons">

                            <?php echo CHtml::ajaxButton($model->isNewRecord ? 'Создать' : 'Сохранить', CHtml::normalizeUrl(array('malefactor/create')),

                                        array(

                                            'success' => 'updateMailFactorFields', 'type' => 'POST', 'cache' => false, 'data' => 'js:$("#malefactor-form").serialize()'

                                        ), array(

                                            'type' => 'submit'

                                        ) )?>

                    </div>


            <?php $this->endWidget(); ?>


            </div><!-- form -->

        </div>

    </div>

<!--    <div id="overflow"></div>-->

</div>

Форма отображается клиетам при нажатии на одну магическую кропку на панели но это не важно… Эту форма обрабатывает методом actionCreate контроллера




public function actionCreate()

	{

		$model=new Malefactor;

                if (Yii::app()->request->isAjaxRequest) {

                    $this->performAjaxValidation($model);

                    if(isset($_POST['Malefactor']))

                    {

                            $model->attributes=$_POST['Malefactor'];

                            if($model->save()) {

                                

                                Yii::app()->end();

                                return;

                            } else {

                                print_r($_POST); echo "||";

                                $this->renderPartial('ajaxForm', array('model' => $model), false, true);

                                return;

                            }

                    }

                    Yii::app()->end();

                    

                } else {

                    // Uncomment the following line if AJAX validation is needed

                    // $this->performAjaxValidation($model);


                    if(isset($_POST['Malefactor']))

                    {

                            $model->attributes=$_POST['Malefactor'];

                            if($model->save())

                                    $this->redirect(array('view','id'=>$model->Id));

                    }


                    $this->render('create',array(

                            'model'=>$model,

                    ));

                }

		

	}



С первого взгляда все вроде работает хорошо. Но не все так хорошо как хотелось бы. В таблице оказывается не одна строка, а N-ое количество строк.

Я стал проверять стандартным echo, при первой попытке сохронить пустую форму, выводится Array ( [Malefactor] => Array ( [FLP] => [RegistrationPassportAddress] => ) ) || при последующих попытках сохранить форму вывод echo удваеваются.

Помоги пожалуйста разобраться в этом деле.

Как раз это очень может быть важным. Возможно если вы подгружаете форму через ajax то может быть ситуация когда на вашу кнопку отправки формы

<?php echo CHtml::ajaxButton( …

может быть повешено несколько одинаковых функций на событие submit. Попробуйте присмотреться к javascript сколько раз он отправляет форму. Если действительно несколько раз то вам поможет функция $(селектор).unbind();

Вот как я боролся с этим

[html]<div class="row buttons">

&lt;?php


    echo CHtml::submitButton('Добавить',array('id'=&gt;'sendform-addComment'));


    Yii::app()-&gt;clientScript-&gt;registerScript('sendform-addComment',&quot;


        var clickSendFormAddComment = function() {


            


            // Вот здесь убиваются лишнии функции clickSendFormAddComment на кнопке отправки, которые 


            // могут возникнуть в результате когда код формы был несколько раз загружен через ajax


            &#036;('#sendform-addComment').unbind(); 





            jQuery.ajax({


                'success':function(data) {


                    &#036;('#AddComments .form-comment').empty();


                    &#036;('#AddComments .form-comment').append(data);


                },


                'type':'POST',


                'url':'&quot;.&#036;this-&gt;createUrl('/comments/default/addcomment').&quot;',


                'cache':false,


                'data':jQuery(this).parents('form').serialize()


            });


            return false;


        }





        &#036;('#sendform-addComment').click(clickSendFormAddComment);


    &quot;);


?&gt;

</div>[/html]

Если это не помогает то в любом случае начать надо с того что смотреть что отправляет javascript

Сколько я бился, чтоб избавиться от дублирования POST! Только ваш ответ на форуме помог. Я пробовал сделать форму с кнопкой ajaxSubmitButton, чтоб по нажатию появлялась другая форма также с ajaxSubmitButton отменяющего действия. Почему-то каждый раз передаётся не текущий POST запрос, а все "фантомные", которые были на этой кнопке отправлены ранее. Мне нужно было сделать механизм сохранения/отмены: [Сохранил], [Отменил], [Сохранил], [Отменил] и так до бесконечности. А получалось в запросах POST такая фигня: [Сохранил], [Отменил], [Сохранил, Сохранил], [Отменил, Отменил], [Сохранил, Сохранил, Сохранил], [Отменил, Отменил, Отменил] и так далее…

Было реализовано так. Во View:




echo CHtml::ajaxSubmitButton('Заказал:',

    CHtml::normalizeUrl(array('createRow','render'=>false)),

    array('success'=>'js: function(data) {

        $("#ordered-row-' . $model->inventory . '").html(data);

    }'),

    array(

        'id'=>'row-btn-' . $model->inventory,

        'name' => 'create-submit',

        'class' => 'btn btn-success span-3 form_submit'

    ));



В Controller была функция, которая сохраняла запись и подменяла форму.

После мытарств модернизировал немного (меньше селекторов jQuery) приведённый тут вариант и всё заработало (может, кому пригодится):




$this->widget('bootstrap.widgets.TbButton',array(

      'id'=>'row-btn-' . $model->inventory,

      'label'=>Yii::t("main", "Ordered:"),

      'type'=>'success'

  ));

  Yii::app()->clientScript->registerScript('row-btn-' . $model->inventory,"

      var clickSendFormCreateRow = function() {

          // Вот здесь убиваются лишнии функции clickSendFormCreateRow на кнопке отправки, которые 

          // могут возникнуть в результате когда код формы был несколько раз загружен через ajax

          $('#row-btn-" . $model->inventory . "').unbind(); 

          jQuery.ajax({

              'success':function(data) {

                  $('#ordered-row-" . $model->inventory . "').html(data);

              },

              'type':'POST',

              'url':'".$this->createUrl('/ordered/createRow')."',

              'cache':false,

              'data':jQuery(this).parents('form').serialize()

          });

          return false;

      }

      $('#row-btn-" . $model->inventory . "').click(clickSendFormCreateRow);

  ");



Возможно я просто везучий, и то что напишу ниже не совсем "втему", но у меня такие конструкции работают отлично.

  1. Почему не используете clientValidation и validateOnSubmit в вашей форме?

  2. Рекомендую присмотреться к переменной ajaxVar, она же будет передаваться в POST в виде ‘ajax’=>‘my-form’. Именно она служит для распознавания чем вызван запрос - ajax валидацией или submit.

  3. Снова таки рекомендую пересмотреть собственно ваш action. Я пишу такие вещи по стандарту:




   $model=new Malefactor;

   if (Yii::app()->request->getIsAjaxRequest()) {

        $error=CActiveForm::validate($model);

            if($error!='[]'){

                echo $error;

                Yii::app()->end();

            }

    }

   if (isset($_POST[get_class($model)])&&($_POST['ajax']!='malefactor-form'))//именно эта строчка спасла меня от дублирований в свое время

   {

      //ваш код по обработке

   } 



  1. Используйте ajaxSubmit. При использовании аякса нужно следить за уникальностями id элементов. Я делаю это так:



//в htmlOptions добавляем

 'id' => 'malefactor-form_submit_' . rand(1, 255),



Это нужно для того чтобы предотвратить конфликты при нескольких загрузках.

Надеюсь написанное выше будет полезно Вам или кому-то еще.

P.S. Предыдущим комментаторам, я не осуждаю ваш метод но - вешать click на submit кнопку это написание велосипеда. Собственно из за этого и возникают проблемы, так как вы пытаетесь переписать механизм сабмита фоормы. При этом переменная ajaxVar не инициализируется и определить что вызвало запрос становится невозможно. Именно из за этого и происходит дублирование: 1 раз проходит аякс валидация, 2 раз сабмит.