Search code examples
phpcodeigniteractiverecordcodeigniter-2

Codeigniter update_batch() with included update of the where key


I want to update multiple rows in the database with codeigniters update_batch() function.
But the field specified in the where should also be changed.

The following code should make it clear:

$set = array(
  array(
    'token'           => '65787131678754',
    'device'          => 'none',
    'new_token_value' => ''
  ),
  array(
    'token'           => '75798451315464',
    'device'          => 'none',
    'new_token_value' => ''
  )
);

$this->db->update_batch(TBL_NAME, $set, 'token');

Tokens specified in token should be updated with device to 'none' and the token itself should be set to empty string ''.

Is this possible with update_batch() function?


In sql I would write something like

UPDATE TBL_NAME
SET token='', device='none'
WHERE token='65787131678754'

for one update but this is not practicable for multiple, so I want to use the update_batch() function.


Solution

  • I created a helper function mostly identical to the codeigniter batch_update() function.
    But with the ability to update the index itself. The new value is defined by index_update_key.

    function update_batch($db, $table = '', $set = NULL, $index = NULL, $index_update_key = '') {
    if ($table === '' || is_null($set) || is_null($index) || !is_array($set)) {
        return FALSE;
    }
    
    $sql = 'UPDATE ' . $db->protect_identifiers($table) . ' SET ';
    
    $ids = $when = array();
    $cases = '';
    
    //generate the WHEN statements from the set array
    foreach ($set as $key => $val) {
        $ids[] = $val[$index];
    
        foreach (array_keys($val) as $field) {
            if ($field != $index && $field != $index_update_key) {
                $when[$field][] = 'WHEN ' . $db->protect_identifiers($index) 
                                . ' = ' . $db->escape($val[$index]) . ' THEN ' . $db->escape($val[$field]);
            } elseif ($field == $index) {
                //if index should also be updated use the new value specified by index_update_key
                $when[$field][] = 'WHEN ' . $db->protect_identifiers($index) 
                                . ' = ' . $db->escape($val[$index]) . ' THEN ' . $db->escape($val[$index_update_key]);
            }
        }
    }
    
    //generate the case statements with the keys and values from the when array
    foreach ($when as $k => $v) {
        $cases .= "\n" . $db->protect_identifiers($k) . ' = CASE ' . "\n";
        foreach ($v as $row) {
            $cases .= $row . "\n";
        }
    
        $cases .= 'ELSE ' . $k . ' END, ';
     }
    
     $sql .= substr($cases, 0, -2) . "\n"; //remove the comma of the last case
     $sql .= ' WHERE ' . $index . ' IN (' . implode(',', $ids) . ')';
    
     return $db->query($sql);
    }
    

    Now I can do the following

    $set = array(
      array(
        'token'           => '657871316787544',
        'device'          => 'none',
        'new_token_value' => ''
      ),
      array(
        'token'           => '757984513154644',
        'device'          => 'none',
        'new_token_value' => ''
      )
    );
    
    update_batch($this->db, 'table_name', $set, 'token', 'new_token_value');
    

    and the sql output is

    UPDATE `b2c` SET 
    `token` = CASE 
    WHEN `token` = '657871316787544' THEN ''
    WHEN `token` = '757984513154644' THEN ''
    ELSE token END, 
    `device` = CASE 
    WHEN `token` = '657871316787544' THEN 'none'
    WHEN `token` = '757984513154644' THEN 'none'
    ELSE device END
    WHERE token IN (657871316787544,757984513154644)