2015-10-07 145 views
2

例如:我們有測試表,它有3列:id,watchers,title。 我們有一個代碼:如何在Yii中保存數據並確保正確保存?

$test = Test::model()->findByPk(1); 
echo $test->watchers; // 0 
$test->title = 'another'; 
$test->save(); 

當我們調用save()IR產生像"UPDATE test SET title='another', watchers='0' WHERE id='1'" SQL查詢。所以,一切接縫都可以。但問題是,如果另一個進程在findByPk和保存在當前腳本之間的時間內更新watchers變量,代碼將生成錯誤的值。所以:

$test = Test::model()->findByPk(1); 
echo $test->watchers; // 0 
$test->title = 'another'; 
//HERE WE HAVE A CODE WHICH PERFORMS FOR 1 SECOND. MEANWHILE ANOTHER PROCESS 
// UPDATES TABLE WITH WATCHERS = 1 
$test->save(); 

所以,這段代碼會將記錄的觀察者字段保存回0.如何克服這個問題?爲什麼Yii ORM不保存只有更改了值?爲什麼它試圖保存所有值?謝謝。

+1

我建議@Criesto的答案,你也可以使用TRANSACTIN(如果數據庫引擎是InnoDB):http://www.yiiframework.com/doc/api/1.1/CDbTransaction它阻塞表,所以另一個查詢必須「等待」事務提交或回滾之前。 –

+0

@ Criesto的回答不好,因爲我不想在存儲數據的地方重寫每一段代碼。這個問題是項目範圍內的,我必須重寫所有代碼,而不僅僅是關鍵。關於交易 - 您確定將交易添加到每一次更新是一個好主意嗎?在任何情況下,事務都不能解決問題,因爲問題在於由Yii生成的錯誤代碼,而不是MySQL的一致性。需要修補Yii。事實上,save()方法在Yii2中的工作方式完全相同,這證明了我的建議。 – Volodymyr

+0

您可以從CActiveRecord(f.e. ActiveRecord)擴展的類來擴展所有的模型。所以你只需要將模型的父類改爲ActiveRecord並在ActiveRecord中重寫save()方法。您也可以根據需要覆蓋其他使用數據庫的方法,並且無需在使用CRUD方法的情況下更改整個代碼。 –

回答

2

由於您在$test中獲得了每個值,所以當您執行$test->save();時,每個屬性都會被保存爲新記錄或其包含的上一個值。

當您查詢$test = Test::model()->findByPk(1);然後$test->watchers;將其在那裏當你執行的查詢相同的值,這個值只會更改(如果觀察者的價值被另一個update查詢更改)當你這樣做的另一個select查詢。希望是有道理的:P

你可以試試下面的更新方法:

Test::model()->updateByPk(1, array(
       'title' => 'another' 
      )); 

將執行以下查詢:

UPDATE `test` SET `title`=:yp0 WHERE `test`.`id`=1. 
Bound with :yp0='another' 
1

我會解決這個問題,因爲這樣的:

​​

{或}

$test = Test::model()->findByPk(1); 
    $test->title = 'another'; 

    /*according to the api the parameter only saves the columns 
     that are mentioned in the array. In this case it will save just the 
     title 
    */ 

    $test->saveAttributes(array('title')); 
0

至於我,最好的解決方案是繼承和重寫save()方法,並且它的行爲與Yii2(getDirtyAttributes()方法)中的行爲相同。 W必須比較屬性以從db屬性中獲取並保存那些被修改的屬性。我已經成功實現了這一點,並可以確認它的工作原理。