2017-05-15 72 views
4

我們有兩個模型「Foo」和「Bar」,如下圖所示(一個Foo可以有很多條)。更新一對多關係條目而不觸及未受影響的行

database tables scheme

比方說,我們想從$_POST更新Foo模型值。由於FooBar的關係,我們也想要更新Bar模型,其值爲相同的$_POSTFoo更新過程與往常一樣(即通過foo ID從數據庫加載Foo模型,然後加載$_POSTFoo模型並保存模型)。但Foo可以有很多Bar s,這就是爲什麼我們刪除條形表中有$fooId的所有條目並從$_POST創建條形表的新條目。

例如,用戶打開表單,他可以在其中更改Foo名稱並添加/更改/刪除多個欄名稱。假設當前的foo名稱是「foo」,當前的條形是「bar1」,「bar2」和「bar3」。用戶將foo名稱更改爲「fooChanged」,同時將「bar1」更改爲「bar10」並刪除「bar3」。 注意:沒有觸及「bar2」。提交表單後,控制器加載Foo模型,並將其加載到foo中(現在「foo」已更改爲「fooChanged」)並保存模型。與Bar模型它有點不同。首先,控制器刪除所有具有$fooId的條目,並用batchInsert(參見下面的代碼)創建新條目。

控制器:

public function actionUpdateFoo($fooId = null) 
{ 
    $foo = Foo::findOne($fooId); 
    $foo->load(Yii::$app->request->post()); 

    $transaction = Yii::$app->db->beginTransaction(); 
    if ($foo->save() && Bar::deleteAll(['foo_id' => $fooId]) && Bar::create($fooId, Yii::$app->request->post())) { 
     $transaction->commit(); 
    } else { 
     $transaction->rollBack(); 
    } 

    return $this->render('foo', [ 
     'foo' => $foo, 
    ]); 
} 

酒吧型號:

public static function create($fooId, $post) 
{ 
    $array = []; 
    foreach ($post['Bar'] as $item) { 
     array_push($array, [ 
      'foo_id' => $fooId, 
      'name' => $item['name'], 
     ]); 
    } 

    return Yii::$app->db->createCommand()->batchInsert(self::tableName(), ['foo_id', 'name'], $array)->execute(); 
} 

我們面臨的問題是,爲了更新許多Bar條目,我們必須刪除舊的和添加新的。我們認爲這種方法並不是最優的,因爲如果我們有很多條目和用戶更改,我們必須刪除所有條目並再次插入相同的條目和更新的條目。 (就像上面的例子,即使「bar2」未被觸摸,所有三個Bar條目也將被刪除)。

是否還有其他更好的方法比這一個(我們想忽略不變的行,只改變受影響的行)?

+0

您可能會考慮只刪除不在提交的$ _POST中的Bar。之後,插入忽略或替換爲只添加新條。 – xangxiong

+0

假設您使用$ bar獲取相關的$ foo數據,當您通過id獲取$ foo進行更新時,請創建一個比較算法,查看$ _POST數組與$ foo相關$ bar數據中的數據。然後你會知道該忽略什麼以及要更新什麼。 – dataskills

+0

我會跟蹤ID(s)...添加隱藏的ID字段,當條形圖條目被提交時,會發布該隱藏的ID字段,與數據庫進行比較,如果有更改,則會更新這些條目。幫助? – Hmmm

回答

2

不需要先刪除所有行,然後再次添加。我們正在使用一種簡單的方法來檢測更改並只更新更新的行。雖然這可能不會減少用代碼編寫的行數,但它減少了可能提高加載速度的查詢量。

簡短摘要actionUpdateFoo($fooId = null)

我們正在加載Foo用新值。我們還選擇分配給Foo模型的所有Bars。使用foreach()我們遍歷Bar並將每個找到的行的ID放置到一個變量($dependantBars)。使用方法,我們(總是)得到一個大小爲2的數組(第一個元素是舊值數組,第二個元素是新值數組)。在if()中,我們保存更新的Foo模型,並檢查刪除和插入是否成功。

/** 
* Let's say, we have in this example: 
* $dependantBars = [0, 1, 2, 3]; (old choices) 
* $foo['choices'] = [0, 1, 5, 7]; (loaded from Yii::$app->request->post()['Foo']['choices']) 
*/ 
public function actionUpdateFoo($fooId = null) 
{ 
    $foo = Foo::findOne($fooId); 
    $foo->load(Yii::$app->request->post()); 

    $subFoo = Bar::findAll($fooId); 
    $dependantBars = []; 
    foreach ($subFoo as $foo) { 
     $dependantBars[] = $foo->id; 
    } 

    $distinction = self::getDifferencesInArrays($dependantBars, $foo['choices']); 

    $transaction = Yii::$app->db->beginTransaction(); 
    if ($foo->save() && Bar::updateUserChoices($distinction)) { 
     $transaction->commit(); 
    } else { 
     $transaction->rollBack(); 
    } 

    // Do something else 
} 

在控制器分離方法來獲得差異:

/** 
* Checks the difference between 2 arrays. 
* 
* @param array $array1 Old values that are still saved in database. 
* @param array $array2 New values that are selected by user. 
* @return array 
*/ 
public static function getDifferencesInArrays($array1 = [], $array2 = []) 
{ 
    return [ 
     array_diff($array1, $array2), 
     array_diff($array2, $array1), 
    ]; 
} 

而在酒吧類,我們可以寫這個方法做兩件事以同樣方法(刪除和插入):

public static function updateUserChoices($distinction) 
{ 
    $deletedRows = true; 
    $insertedRows = true; 

    if (!empty($distinction[0])) { 
     $deletedRows = self::deleteAll(['foo_id' => $distinction[0]]); 
    } 

    if (!empty($distinction[1])) { 
     $insertedRows = Bar::create(); // Something here 
    } 

    return $deletedRows && $insertedRows; 
} 
1

我寫了一個擴展來加載並保存一個多維數組來執行此操作:https://github.com/e-frank/yii2-data

您可以在增量和完整/完整關係之間進行選擇,並將缺失/刪除​​的相關行刪除或取消關聯。 反饋歡迎。