Comments are being saved twice

I am new to Yii but I have traced the problem or the code doing this to be




public function addComment($comment)

	{

		$comment->issue_id=$this->id;

		return $comment->save();

	}



because when I comment out


return $comment->save();

and change it to


return true;

nothing happens (logically) but the success message is shown

here’s my issue.php




<?php


/**

 * This is the model class for table "tbl_issue".

 *

 * The followings are the available columns in table 'tbl_issue':

 * @property integer $id

 * @property string $name

 * @property string $description

 * @property integer $project_id

 * @property integer $type_id

 * @property integer $status_id

 * @property integer $owner_id

 * @property integer $requester_id

 * @property string $create_time

 * @property integer $create_user_id

 * @property string $update_time

 * @property integer $update_user_id

 *

 * The followings are the available model relations:

 * @property User $requester

 * @property User $owner

 * @property Project $project

 */

class Issue extends TrackStarActiveRecord

{

	const TYPE_BUG=0;

	const TYPE_FEATURE=1;

	const TYPE_TASK=2;


	const STATUS_NOT_YET_STARTED = 0;

	const STATUS_STARTED = 1;

	const STATUS_FINISHED = 2;


	/**

	 * Returns the static model of the specified AR class.

	 * @return Issue the static model class

	 */

	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}


	/**

	 * @return string the associated database table name

	 */

	public function tableName()

	{

		return 'tbl_issue';

	}


	/**

	 * @return array validation rules for model attributes.

	 */

	public function rules()

	{

		// NOTE: you should only define rules for those attributes that

		// will receive user inputs.

		return array(

			array('name', 'required'),

			array('project_id, type_id, status_id, owner_id, requester_id', 'numerical', 'integerOnly'=>true),

			array('name', 'length', 'max'=>256),

			array('description', 'safe'),

			// The following rule is used by search().

			// Please remove those attributes that should not be searched.

			array('id, name, description, project_id, type_id, status_id, owner_id, requester_id, create_time, create_user_id, update_time, update_user_id', 'safe', 'on'=>'search'),

		);

	}


	/**

	 * @return array relational rules.

	 */

	public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

			'owner' => array(self::BELONGS_TO, 'User', 'owner_id'),

			'project' => array(self::BELONGS_TO, 'Project', 'project_id'),

			'requester' => array(self::BELONGS_TO, 'User', 'requester_id'),

			'comments' => array(self::HAS_MANY, 'Comment', 'issue_id'),

			'commentCount' => array(self::STAT, 'Comment', 'issue_id'),

		);

	}


	/**

	 * @return array customized attribute labels (name=>label)

	 */

	public function attributeLabels()

	{

		return array(

			'id' => 'ID',

			'name' => 'Name',

			'description' => 'Description',

			'project_id' => 'Project',

			'type_id' => 'Type',

			'status_id' => 'Status',

			'owner_id' => 'Owner',

			'requester_id' => 'Requester',

			'create_time' => 'Create Time',

			'create_user_id' => 'Create User',

			'update_time' => 'Update Time',

			'update_user_id' => 'Update User',

		);

	}


	/**

	 * Retrieves a list of models based on the current search/filter conditions.

	 * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.

	 */

	public function search()

	{

		// Warning: Please modify the following code to remove attributes that

		// should not be searched.


		$criteria=new CDbCriteria;


		$criteria->compare('id',$this->id);

		$criteria->compare('name',$this->name,true);

		$criteria->compare('description',$this->description,true);

		$criteria->compare('project_id',$this->project_id);

		$criteria->compare('type_id',$this->type_id);

		$criteria->compare('status_id',$this->status_id);

		$criteria->compare('owner_id',$this->owner_id);

		$criteria->compare('requester_id',$this->requester_id);

		$criteria->compare('create_time',$this->create_time,true);

		$criteria->compare('create_user_id',$this->create_user_id);

		$criteria->compare('update_time',$this->update_time,true);

		$criteria->compare('update_user_id',$this->update_user_id);

		$criteria->condition='project_id=:projectID';

		$criteria->params=array(':projectID'=>$this->project_id);

		return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

		));

	}


	/**

	* @return array issue type names indexed by type IDs

	*/

	public function getTypeOptions()

	{

		return array(

			self::TYPE_BUG=>'Bug',

			self::TYPE_FEATURE=>'Feature',

			self::TYPE_TASK=>'Task',

		);

	}


	/**

	* @return array issue status names indexed by status IDs

	*/

	public function getStatusOptions()

	{

		return array(

			self::STATUS_NOT_YET_STARTED => 'Not Yet Started',

			self::STATUS_STARTED => 'Started',

			self::STATUS_FINISHED => 'Finished',

		);

	}


	/**

	* @return string the status text display for the current issue

	*/

	public function getStatusText()

	{

		$statusOptions=$this->statusOptions;

		return isset($statusOptions[$this->status_id]) ? $statusOptions[$this->status_id] : "unknown status ({$this->status_id})";

	}


	/**

	* @return string the type text display for the current issue

	*/

	public function getTypeText()

	{

		$typeOptions=$this->typeOptions;

		return isset($typeOptions[$this->type_id]) ? $typeOptions[$this->type_id] : "unknown type ({$this->type_id})";

	}


	/**

	* Adds a comment to this issue

	*/

	public function addComment($comment)

	{

		$comment->issue_id=$this->id;

		return $comment->save();

	}


}



my issuecontroller.php




<?php


class IssueController extends Controller

{

	/**

	 * @var string the default layout for the views. Defaults to '//layouts/column2', meaning

	 * using two-column layout. See 'protected/views/layouts/column2.php'.

	 */

	public $layout='//layouts/column2';


	/**

	* @var private property containing the associated Project model

	instance.

	*/

	private $_project = null;


	/**

	* Protected method to load the associated Project model class

	* @project_id the primary identifier of the associated Project

	* @return object the Project data model based on the primary key

	*/

	protected function loadProject($project_id)

	{

	//if the project property is null, create it based on input id

		if($this->_project===null)

		{

			$this->_project=Project::model()->findbyPk($project_id);

			if($this->_project===null)

			{

				throw new CHttpException(404,'The requested project does not exist.');

			}

		}

		return $this->_project;

	}




	/**

	 * @return array action filters

	 */

	public function filters()

	{

		return array(

			'accessControl', // perform access control for CRUD operations

			'projectContext + create index admin', //perform a check to ensure valid project context

		);

	}


	/**

	 * Specifies the access control rules.

	 * This method is used by the 'accessControl' filter.

	 * @return array access control rules

	 */

	public function accessRules()

	{

		return array(

			array('allow', // allow authenticated user to perform 'create' and 'update' actions

				'actions'=>array('index','view', 'create','update'),

				'users'=>array('@'),

			),

			array('allow', // allow admin user to perform 'admin' and 'delete' actions

				'actions'=>array('admin','delete'),

				'users'=>array('admin'),

			),

			array('deny',  // deny all users

				'users'=>array('*'),

			),

		);

	}


	/**

	 * Creates a new model.

	 * If creation is successful, the browser will be redirected to the 'view' page.

	 */

	public function actionCreate()

	{

		$model=new Issue;

		$model->project_id = $this->_project->id;


		// Uncomment the following line if AJAX validation is needed

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


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

		{

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

			if($model->save())

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

		}


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

			'model'=>$model,

		));

	}


	/**

	 * Updates a particular model.

	 * If update is successful, the browser will be redirected to the 'view' page.

	 * @param integer $id the ID of the model to be updated

	 */

	public function actionUpdate($id)

	{

		$model=$this->loadModel($id, true);

		$this->loadProject($model->project->id);




		// Uncomment the following line if AJAX validation is needed

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


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

		{

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

			if($model->save())

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

		}


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

			'model'=>$model,

		));

	}


	/**

	 * Deletes a particular model.

	 * If deletion is successful, the browser will be redirected to the 'admin' page.

	 * @param integer $id the ID of the model to be deleted

	 */

	public function actionDelete($id)

	{

		if(Yii::app()->request->isPostRequest)

		{

			// we only allow deletion via POST request

			$this->loadModel($id)->delete();


			// if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser

			if(!isset($_GET['ajax']))

				$this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin'));

		}

		else

			throw new CHttpException(400,'Invalid request. Please do not repeat this request again.');

	}


	/**

	 * Lists all models.

	 */

	public function actionIndex()

	{

		$dataProvider=new CActiveDataProvider('Issue', array(

				'criteria'=>array(

				'condition'=>'project_id=:projectId',

				'params'=>array(':projectId'=>$this->_project->id),

			),

		));

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

			'dataProvider'=>$dataProvider,

		));

	}


	/**

	 * Manages all models.

	 */

	public function actionAdmin()

	{

		$model=new Issue('search');

		$model->unsetAttributes();  // clear any default values

		if(isset($_GET['Issue']))

			$model->attributes=$_GET['Issue'];


		$model->project_id = $this->_project->id;


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

			'model'=>$model,

		));

	}


	/**

	 * Returns the data model based on the primary key given in the GET variable.

	 * If the data model is not found, an HTTP exception will be raised.

	 * @param integer the ID of the model to be loaded

	 */

	 /* ORIG before page 231

	public function loadModel($id)

	{

		$model=Issue::model()->findByPk((int)$id);

		if($model===null)

			throw new CHttpException(404,'The requested page does not exist.');

		return $model;

	}

	*/

	public function loadModel($id, $withComments=false)

	{

			if(isset($id))

			{

				if($withComments)

				{

					$model=Issue::model()->with(array('comments'=>array('with'=>'author')))->findbyPk($id);

				}

				else

				{

					$model=Issue::model()->findbyPk($id);

				}

			}

			if($model===null)

				throw new CHttpException(404,'The requested page does not exist.');

		return $model;

	}




	/**

	 * Performs the AJAX validation.

	 * @param CModel the model to be validated

	 */

	protected function performAjaxValidation($model)

	{

		if(isset($_POST['ajax']) && $_POST['ajax']==='issue-form')

		{

			echo CActiveForm::validate($model);

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

		}

	}


	/**

	* In-class defined filter method, configured for use in the above filters() method

	* It is called before the actionCreate() action method is run in order to ensure a proper project context

	*/

	public function filterProjectContext($filterChain)

	{

		//set the project identifier based on either the GET or POST input

		//request variables, since we allow both types for our actions

		$projectId = null;

		if(isset($_GET['pid']))

			$projectId = $_GET['pid'];

		elseif(isset($_POST['pid']))

			$projectId = $_POST['pid'];

		$this->loadProject($projectId);

		//complete the running of other filters and execute the requested action

		$filterChain->run();

	}


	/**

	 * Displays a particular model.

	 * @param integer $id the ID of the model to be displayed

	 */

	public function actionView($id)

	{

		$issue=$this->loadModel($id, true);

		$comment=$this->createComment($issue);


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

			'model'=>$this->loadModel($id, true),

			'comment'=>$comment,

		));

	}


	/**

	* Returns the project model instance to which this issue belongs

	*/

	public function getProject()

	{

		return $this->_project;

	}


	protected function createComment($issue)

	{

		$comment=new Comment;

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

		{

			$comment->attributes=$_POST['Comment'];

			if($issue->addComment($comment))

			{

				Yii::app()->user->setFlash('commentSubmitted',"Your comment has been added." );

				$this->refresh();

			}

		}

		return $comment;

	}


}



views/issue/view.php




<?php

$this->breadcrumbs=array(

	'Issues'=>array('index'),

	$model->name,

);


$this->menu=array(

	array('label'=>'List Issue', 'url'=>array('index', 'pid'=>$model->project->id)),

	array('label'=>'Create Issue', 'url'=>array( 'create', 'pid'=>$model->project->id)),

	array('label'=>'Update Issue', 'url'=>array('update', 'id'=>$model->id)),

	array('label'=>'Delete Issue', 'url'=>'#', 'linkOptions'=>array('submit'=>array('delete','id'=>$model->id),'confirm'=>'Are you sure you want to delete this item?')),

	array('label'=>'Manage Issue', 'url'=>array('admin', 'pid'=>$model->project->id)),

);


?>


<h1>View Issue #<?php echo $model->id; ?></h1>


<?php $this->widget('zii.widgets.CDetailView', array(

	'data'=>$model,

	'attributes'=>array(

		'id',

		'name',

		'description',

		array(

			'name' => 'type_id',

			'value' => CHtml::encode($model -> getTypeText())

		),

		array(

			'name' => 'status_id',

			'value' => CHtml::encode($model -> getStatusText())

		),

		array(

			'name' => 'owner_id',

			'value' => CHtml::encode($model -> owner-> username)

		),

		array(

			'name' => 'requester_id',

			'value' => CHtml::encode($model -> requester -> username)

		),

	),

)); ?>


<div id="comments">

<?php if($model->commentCount>=1): ?>

	<h3>

		<?php echo $model->commentCount>1 ? $model->commentCount . 'comments' : 'One comment'; ?>

	</h3>

	<?php $this->renderPartial('_comments',array('comments'=>$model->comments,)); ?>

<?php endif; ?>

<h3>Leave a Comment</h3>

<?php if(Yii::app()->user->hasFlash('commentSubmitted')): ?>

<div class="flash-success">

	<?php echo Yii::app()->user->getFlash('commentSubmitted'); ?>

</div>

<?php else: ?>

	<?php $this->renderPartial('/comment/_form',array('model'=>$comment,)); ?>

<?php endif; ?>

</div>



and finally my view/comment/_form.php




<div class="form">


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

	'id'=>'comment-form',

	'enableAjaxValidation'=>true,

)); ?>


	<p class="note">Fields with <span class="required">*</span> are required.</p>


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


	<div class="row">

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

		<?php echo $form->textArea($model,'content',array('rows'=>6, 'cols'=>50)); ?>

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

	</div>


	<div class="row buttons">

		<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>

	</div>


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


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



can someone help me shed some light here and if I need to post some more codes

thanks

You’ve probably found the solution. But for others:

In Controller in actions Create and Update you just need find these to lines:

            // Uncomment the following line if AJAX validation is needed


            // &#036;this-&gt;performAjaxValidation(&#036;model);

and as the instruction in the first line says, you need to uncomment the second line.

Or other possibility is to set ajax validation to false in you view file _form.php. You can find this at the very top of the file _form.php:

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

'id'=&gt;'text-form',


'enableAjaxValidation'=&gt;true,

));


didn’t solve the issue…

This was helpful as I was facing the issue

Thank you!

I know this is an old one, but this helped me. Thanks! I can’t believe how simple this turned out to be; I spent a few hours overthinking it, and ajax didn’t dawn on me.

It was the Ajax Validation on the form!! thank you