determination of update or insert

In the update action of my users controller, I would like to put logic of re-sending a verification email if the user changes his/her email address.  For this I need the app to remember the old email to check if it is different.  Of course I could remember the email in a simple variable but I decided to instead use separate AR instances for the original user data and the modified user data, for flexibility and future enhancement.

I found however that Yii thinks that the save() being performed is an insert instead of an update.  I was able to solve this however by setting $updatedUser->isNewRecord = false;

Here is my full code:

<?php


public function actionUpdate() {


	$user = User::model()->findbyPk(isset($_GET['id']) ? $_GET['id'] : Yii::app()->user->id);


	if (empty($user))


		throw new CHttpException(404, 'User cannot be found.');





	if (isset($_POST['User'])) {


		$updatedUser = new User;


		$updatedUser->id = $user->id;


		$updatedUser->isNewRecord = false;


		$updatedUser->setAttributes($_POST['User'], 'update');


		


		if ($updatedUser->validate('update')) {


			


			// email logic here //


			// if ($updatedUser->email!=$user->email) {send verification email logic};


			


			if ($updatedUser->save(false))


				$this->redirect(array('show'));


		}


	}


	$this->render('update', array('user' => $user));


}


Wouldn't it make sense though if Yii automatically knew it was an update instead of an insert because the pk was set?  Just a suggestion… I believe CakePHP uses this logic.

Also, it seems that save() returns false if no rows were updated (for instance if the user presses "submit" without actually changing any fields).  This does not seem to be noted in the documentation and confused me for a bit.  This can be a nice feature, but now I don't know how my app should know the difference between a mysql error and no rows being updated.  I would like the page to redirect in my example regardless of whether the user changed any fields.

Perhaps instead of updateByPk() returning the affected rows, the affected rows should be stored in an AR attribute such as $model->affectedRows or returned in a function?  This is what CakePHP does: http://api.cakephp.o…3038f835e192fc3

Why don't you use "clone" operator to generate your updatedUser instance?

It is not reliable to use PK to determine if a record is new or not. For example, if PK is username and should be entered by end-user, we would have trouble using this approach.

I am not aware about the behavior "save() returns false if no rows were updated". AR doesn't do this. Maybe it's a PDO behavior? It needs more investigation.

I see your argument again.  Clone looks like a better solution anyways.  Thanks

As for save:

looking at the code, it looks like save() actually executes update() (in my case), and returns whatever update() returns.

Update() executes updateByPk() and returns whatever updateByPk() returns.

updateByPk() returns "@return integer the number of rows being updated"

So the return value seems to chain across those three functions, starting at updateByPk()

Yes, the question is why false would be returned at PDO layer when nothing needs to be updated (still need confirmation about this though because I think I never observed this before.)

Because (false == 0) (eg. 0 rows being updated?)  I will double check this though…

Ah, I can confirm this behavior:

Quote

When using UPDATE, MySQL will not update columns where the new value is the same as the old value. This creates the possibility that mysql_affected_rows() may not actually equal the number of rows matched, only the number of rows that were literally affected by the query.

Found here: http://us3.php.net/f…l-affected-rows

Not sure about a good fix though…

Thanks for finding out this. Maybe you should just ignore the return value of AR update because if an error occurs, an exception will be thrown.

I will do that for now.

I also changed CActiveRecord::update() so that it returns true when no error.