Yii Combined Primary Keys with isNewRecord

I have a mysql table with compound keys (user_id, category_id); I am trying to update the last access to these entries as follows

$userCategory = new UserCategory; $userCategory->user_id = 1; $userCategory->category_id = 15; echo $userCategory->isNewRecord; //always true $userCategory->last_access = Now(); $userCategory->save(); 

{$ userCategory-> isNewRecord}, โ€‹โ€‹and when I try to save (), MySQL generates a duplicate error for composite primary keys.

I also added this to the UserCategory model, but didn't help

 public function primaryKey() { return array('user_id', 'category_id'); } 

**** Update: Sorry for the confusion. My question is how to achieve the same result as "ON DUPLICATE KEY UPDATE" in the Yii structure. In other words, how to insert or update in a single SQL query. if you look at the source code save ()

 public function save($runValidation=true,$attributes=null) { if(!$runValidation || $this->validate($attributes)) //checking if new record return $this->getIsNewRecord() ? $this->insert($attributes) : $this->update($attributes);** else return false; } 
+6
source share
5 answers

Actually, the problem is that if isNewRecord always true, it means that Yii will use the INSERT instead of the UPDATE when saving the model to the database .. that is why you always get a duplicate pk error, even if it is compound.

Here is the official documentation for isNewRecord . So the problem is that you are using

 $userCategory = new UserCategory; //Always a new record, tries to INSERT 

So, to solve this problem, you need to find a record and evaluate it before you save it. You can also read the documentation here about the find() family of methods and their return value; the return values โ€‹โ€‹of find () are slightly different in nature:

find .. () returns the found record or NULL if the record is not found.

findAll .. () returns an array containing all the records found, or an empty array if no records were found.

You can use this return value to differentiate an existing primary key:

 $userCategory = UserCategory::model()->findByAttributes(array('user_id '=>1,'category_id '=>15)); // if user does not exist, you need to create it if ($userCategory == NULL) { $userCategory = new UserCategory; $userCategory->user_id = 1; $userCategory->category_id = 15; } echo $userCategory->isNewRecord; //you will see the difference if it does exist or not exist $userCategory->last_access = Now(); $userCategory->save(); 

This ensures that the structure uses the INSERT or UPDATE statements correctly, avoiding duplicating the PK error you get.

Edit: Improved example code to correctly populate a record when it is new.

+7
source

In your model add the following method:

 /** * Uses the primary keys set on a new record to either create or update * a record with those keys to have the last_access value set to the same value * as the current unsaved model. * * Returns the model with the updated last_access. Success can be checked by * examining the isNewRecord property. * * IMPORTANT: This method does not modify the existing model. **/ public function updateRecord(){ $model = self::model()->findByPk(array('user_id'=>$this->user_id,'category_id'=>$this->category_id)); //model is new, so create a copy with the keys set if(null === $model){ //we don't use clone $this as it can leave off behaviors and events $model = new self; $model->user_id = $this->user_id; $model->category_id = $this->category_id; } //At this point we have a model ready for saving, //and don't care if it is new or not $model->last_access = $this->last_access; $model->save(false); return $model; } 

The above method is based on a more general method that I use to create an create-or-find-if-existing process.

To do this, use the following code.

 $userCategory = new UserCategory; $userCategory->user_id = 1; $userCategory->category_id = 15; echo $userCategory->isNewRecord; //always true $userCategory->last_access = Now(); $userCategory = $userCategory->updateRecord(); 

Please note that only the last line is different from your code. The fact that an instance of a model declared with new UserCategory does not change is the intended behavior.

Then you can check in your code whether the model is saved with the following:

 if(!$userCategory->isNewRecord){ echo 'save succeeded'; } else{ echo 'save failed'; } 
+3
source

If you are trying to update, you should download the record, not create a new one.

 UserCategory::model()->findByPk(array('user_id'=> 1,'category_id '=> 15)); $userCategory->last_access = Now(); $userCategory->save(); 
+1
source

in UserCategory.php

 public function isNewRecord() { $result = $this->findByAttributes(array('user_id'=>$this->user_id,'category_id'=>$this->category_id)); if($result === NULL) { return true; } return false; } 

then in the controller

 $userCategory = new UserCategory; $userCategory->user_id = 1; $userCategory->category_id = 15; echo $userCategory->isNewRecord(); ---- 
+1
source

Another option is to change the model to change the condition of the save function, then call the parent save function: (this code is specified in the UserCategory model)

 public function save($runValidation=true,$attributes=null) { $exists = UserCategory::model()->findByAttributes(array('category_id'=>$this->category_id,'user_id'=>$this->user_id)); if($exists) { $this->isNewRecord = false; } return parent::save($runValidation,$attributes); } 

I just did a test and it seems to work correctly. You should just do this:

 $userCategory = new UserCategory; $userCategory->user_id = 1; $userCategory->category_id = 15; $userCategory->last_access = Now(); $userCategory->save(); 

Insert or update based on whether it finds a record, so you don't need to change any other code.

0
source

All Articles