[solucionado] mysqli rollback todos los insert si uno falla

Iniciado por gAb1, 10 Abril 2016, 03:31 AM

0 Miembros y 1 Visitante están viendo este tema.

gAb1

Tengo un script que sube datos a diferentes tablas mysql y necesito que si un insert falla, no se haga ninguno. Para ello he estado leyendo y creo tener una idea clara:

Código (php) [Seleccionar]
$mysqli = new mysqli(/* datos de conexión */);
$mysqli->autocommit(FALSE);

// el rollback lo dejará todo como estaba aquí
$mysqli->begin_transaction();

if ($stmt_uno = $mysqli->prepare('INSERT INTO tabla_principal (uno, dos, tres) VALUES (?, ?, ?)')) {
   $stmt_uno->bind_param('sss', $uno, $dos, $tres);
   $stmt_uno->execute();

   $id = (int) $mysqli->insert_id;

   if ($stmt_dos = $mysqli->prepare('INSERT INTO sub_tabla (principal_id, cuatro) VALUES (?, ?)')) {
       $stmt_dos->bind_param('is', $id, $cuatro);
       $stmt_dos->execute();
   }

   if ($stmt_tres = $mysqli->prepare('INSERT INTO sub_tabla_dos (principal_id, cinco) VALUES (?, ?)')) {
       $stmt_tres->bind_param('is', $id, $cinco);
       $stmt_tres->execute();
   }
}


if ($mysqli->commit()) {
   // todo correcto, se redirige
   header('Location: /');
} else {
   // algo ha salido mal, se llama rollback
   $mysqli->rollback();
   // y se muestra un mensaje de error
}


Todavía no lo he probado, pero no estoy seguro de si es así o hay que comprobar cada execute()...

¿Alguien puede corregirme?

Gracias!

Edito: cambio la estructura, el resto de inserts dependen del primero, pero si cualquiera falla no debe insertarse nada.

Gallu

Hola, lo mejor en estos casos es usar bloques try catch.

http://php.net/manual/es/language.exceptions.php

De esta forma te aseguras de que ante cualquier error se ejecute el rollback
Por ejemplo:

Código (php) [Seleccionar]

$mysqli = new mysqli(/* datos de conexión */);
$mysqli->autocommit(FALSE);

// el rollback lo dejará todo como estaba aquí
$mysqli->begin_transaction();

try{
if ($stmt_uno = $mysqli->prepare('INSERT INTO tabla_principal (uno, dos, tres) VALUES (?, ?, ?)')) {
$stmt_uno->bind_param('sss', $uno, $dos, $tres);
$stmt_uno->execute();

$id = (int) $mysqli->insert_id;

if ($stmt_dos = $mysqli->prepare('INSERT INTO sub_tabla (principal_id, cuatro) VALUES (?, ?)')) {
$stmt_dos->bind_param('is', $id, $cuatro);
$stmt_dos->execute();
}

if ($stmt_tres = $mysqli->prepare('INSERT INTO sub_tabla_dos (principal_id, cinco) VALUES (?, ?)')) {
$stmt_tres->bind_param('is', $id, $cinco);
$stmt_tres->execute();
}
}


if ($mysqli->commit()) {
// todo correcto, se redirige
header('Location: /');


}catch(Exception $error){
    //deshacemos todo
$mysqli->rollback();
echo 'Excepción capturada: ',  $error->getMessage(), "\n";
}
Nadie alcanza la meta con un solo intento, ni perfecciona la vida con una sola rectificación, ni alcanza altura con un solo vuelo.

gAb1

Ah, al fin algo nuevo. Si que son muy utiles esos blockes. Gracias, dejo como lo he hecho:

Código (php) [Seleccionar]
$mysqli = new mysqli(/* datos de conexión */);
$mysqli->autocommit(FALSE);

// el rollback lo dejará todo como estaba aquí
$mysqli->begin_transaction();

try{

    if ($stmt_uno = $mysqli->prepare('INSERT INTO tabla_principal (uno, dos, tres) VALUES (?, ?, ?)')) {
        $stmt_uno->bind_param('sss', $uno, $dos, $tres);

        if (!$stmt->execute()) {
            throw new Exception($stmt->error);
        }

        $id = (int) $mysqli->insert_id;

        if ($stmt_dos = $mysqli->prepare('INSERT INTO sub_tabla (principal_id, cuatro) VALUES (?, ?)')) {
            $stmt_dos->bind_param('is', $id, $cuatro);
            if (!$stmt->execute()) {
                throw new Exception($stmt->error);
            }
        }

        if ($stmt_tres = $mysqli->prepare('INSERT INTO sub_tabla_dos (principal_id, cinco) VALUES (?, ?)')) {
            $stmt_tres->bind_param('is', $id, $cinco);
            if (!$stmt->execute()) {
                throw new Exception($stmt->error);
            }
        }
    }

    if ($mysqli->commit()) {
        $redirect = TRUE;
    } else {
        throw new Exception('Transaction commit failed. Property ID: ' . $id);
    }

} catch (Exception $e) {
    try {
        $mysqli->rollback();
        $err_msg = $e->getMessage();
    } catch (Exception $f) {
        $err_msg = $f->getMessage();
    }
    die($usr_msg);
}

// es importante volver a activar el autocommit
$mysqli->autocommit(TRUE);


Me costó un buen rato darme cuenta de por qué no se guardaba nada en la db  :-\