我有一个带有复合键的mysql表(user_id,category_id); 我正在尝试更新这些记录的最后一次访问,如下所示
$userCategory = new UserCategory;
$userCategory->user_id = 1;
$userCategory->category_id = 15;
echo $userCategory->isNewRecord; //always true
$userCategory->last_access = Now();
$userCategory->save();
{$ userCategory-> isNewRecord},当我尝试save()时,MySQL会为复合主键生成重复错误。
我还将此添加到UserCategory模型但没有帮助
public function primaryKey() {
return array('user_id', 'category_id');
}
****更新: 对困惑感到抱歉。我的问题是如何在Yii框架中实现与“ON DUPLICATE KEY UPDATE”相同的结果。换句话说,如何在一个SQL查询中插入或更新。如果你看一下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;
}
答案 0 :(得分:7)
实际上,问题是如果isNewRecord
始终为true,则意味着在将模型保存到数据库时,Yii将使用INSERT
语句而不是UPDATE
语句..这就是为什么你总是得到重复的pk错误,即使它是复合的。
Here是关于IsNewRecord
的官方文档。所以,问题是你正在使用
$userCategory = new UserCategory; //Always a new record, tries to INSERT
因此,要解决此问题,您必须找到记录并在保存之前评估是否找到记录。有关find()
方法系列及其返回值的文档也可以阅读Here,find()方法的返回值因其性质而略有不同:
find ..()返回找到的记录,如果没有找到记录则返回
NULL
。findAll ..()返回一个包含找到的所有记录的数组,如果没有找到记录,则返回一个空数组。
您可以使用此返回值来区分主键是否存在:
$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();
这将确保框架正确使用INSERT或UPDATE语句,避免您遇到重复的PK错误。
修改:增强了示例代码,以便在新记录时正确填充记录。
答案 1 :(得分:3)
在您的模型中,添加以下方法:
/**
* 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;
}
以上内容的灵感来自于一种更为通用的方法,我使用它来创建或查找已存在的过程。
使用以下代码执行此操作。
$userCategory = new UserCategory;
$userCategory->user_id = 1;
$userCategory->category_id = 15;
echo $userCategory->isNewRecord; //always true
$userCategory->last_access = Now();
$userCategory = $userCategory->updateRecord();
请注意,只有最后一行与您的代码不同。使用new UserCategory
声明的模型实例未被更改的事实是预期的行为。
然后,您可以在代码中验证模型是否使用以下内容保存:
if(!$userCategory->isNewRecord){
echo 'save succeeded';
}
else{
echo 'save failed';
}
答案 2 :(得分:1)
如果您正在尝试更新,则应加载记录,而不是创建新记录。
UserCategory::model()->findByPk(array('user_id'=> 1,'category_id '=> 15));
$userCategory->last_access = Now();
$userCategory->save();
答案 3 :(得分:1)
public function isNewRecord()
{
$result = $this->findByAttributes(array('user_id'=>$this->user_id,'category_id'=>$this->category_id));
if($result === NULL)
{
return true;
}
return false;
}
然后在控制器中
$userCategory = new UserCategory;
$userCategory->user_id = 1;
$userCategory->category_id = 15;
echo $userCategory->isNewRecord();
----
答案 4 :(得分:0)
另一种选择是修改模型以更改保存功能的条件,然后调用父保存功能:(此代码进入UserCategory模型)
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);
}
我刚做了一个测试,似乎工作正常。你应该能够做到这一点:
$userCategory = new UserCategory;
$userCategory->user_id = 1;
$userCategory->category_id = 15;
$userCategory->last_access = Now();
$userCategory->save();
应根据是否找到记录来插入或更新,因此您不必更改任何其他代码。