Search code examples
phpfat-free-framework

is save() necessary when commit() is used?


I realized that save() and commit() will perform the same database transaction twice.

Eg.

if (isset($userId)) 
{
    $update = true;
    $user->load(array('id=:id', array(':id' => $userId)));
}

$user->designation_id = $f3->get('POST.designation_id');
$user->firstname = $f3->get('POST.firstname');
$user->lastname = $f3->get('POST.lastname');

$user->save(); //update made to users table

$this->db->begin();

foreach ($userTeams as $teamId) 
{
     $sqlArray[] = sprintf('INSERT INTO %s (internal_team_id, users_id) VALUES (%d,%d)', 'users_internal_team', $teamId, $user->get('_id'));
}
$this->db->exec($sqlArray); //update made to some other table

$this->db->commit();

echo $this->db->log();
die();

The log will show me the following transactions executed in "users" table:

...other log information related to other tables..

UPDATE `users` SET `designation_id`=3,`lastname`='Mali' WHERE `id`=134 Wed, 11 Apr 2018 11:39:20 +0200 (0.5ms) 
UPDATE `users` SET `designation_id`=3,`lastname`='Mali' WHERE `id`=134

So does this mean that I can remove $user->save() and $this->db->commit() will do the same for me?


Solution

  • TL;DR

    save() and commit() are two different things, so you shouldn't skip save().

    SQL background

    In SQL, you can write:

    UPDATE users SET designation_id=3,lastname='Mali' WHERE id=134;
    INSERT INTO users_internal_team (internal_team_id, users_id) VALUES;(999,134)
    

    Or you can write:

    BEGIN;
    UPDATE users SET designation_id=3,lastname='Mali' WHERE id=134;
    INSERT INTO users_internal_team (internal_team_id, users_id) VALUES;(999,134)
    COMMIT;
    

    The difference being that, in the first example, if an error occured during the INSERT statement (like a duplicate entry), the UPDATE statement wouldn't be cancelled (aka "rolled back"), while in the second example it would.

    In F3, the equivalent examples would be:

    $db->exec('UPDATE users etc.');
    $db->exec('INSERT INTO users_internal_team etc.');
    

    and

    $db->begin();
    $db->exec('UPDATE users etc.');
    $db->exec('INSERT INTO users_internal_team etc.');
    $db->commit();
    

    Note that, if you pass an array of statements to the exec() method, they will automatically be embedded inside a transaction:

    $db->exec([
      'UPDATE users etc.',
      'INSERT INTO users_internal_team etc.',
    ]);
    // automatically calls $db->begin() and $db->commit()
    

    Your code

    In your code, you're calling user->save(), which triggers either a $db->exec('INSERT etc.') or a $db->exec('UPDATE etc.'), depending on the result of the preceding $user->load().

    That's one statement.

    The fact that two identical statements appear in the log and that one of them still shows up when you skip the call to $user->save() let me think that $sqlArray must be containing that same statement.

    I may be wrong on the latter point, but in any case, the sequence you're using is correct.