7

我有一个带有复合键( user_id , category_id )的 mysql 表;我正在尝试更新这些记录的最后访问权限,如下所示

$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;
}
4

5 回答 5

7

实际上,问题在于 ifisNewRecord始终为真,这意味着 Yii 在将模型保存到数据库时将使用INSERT语句而不是UPDATE语句。这就是为什么你总是会得到重复的 pk 错误,即使它是复合的。

是关于IsNewRecord. 所以,问题是你正在使用

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

因此,要解决此问题,您必须找到记录并评估是否在保存之前找到它。也可以在此处阅读有关find()方法系列及其返回值的文档,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 错误。

编辑:增强示例代码以在新记录时正确填充记录。

于 2013-03-07T20:54:53.320 回答
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';
}
于 2013-03-07T15:35:27.800 回答
1

在 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;
}

然后在控制器中

$userCategory = new UserCategory;
$userCategory->user_id = 1;
$userCategory->category_id = 15;
echo $userCategory->isNewRecord(); 
----
于 2013-03-07T22:59:48.087 回答
1

如果您尝试更新,则应加载记录,而不是创建新记录。

UserCategory::model()->findByPk(array('user_id'=> 1,'category_id '=> 15));
$userCategory->last_access = Now();
$userCategory->save();
于 2013-03-04T13:04:18.047 回答
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();

应该根据是否找到记录来插入或更新,因此您不必更改任何其他代码。

于 2013-03-08T15:38:20.670 回答