Test Foro de elhacker.net SMF 2.1

Programación => Desarrollo Web => PHP => Mensaje iniciado por: gAb1 en 10 Abril 2016, 03:31 AM

Título: [solucionado] mysqli rollback todos los insert si uno falla
Publicado por: gAb1 en 10 Abril 2016, 03:31 AM
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.
Título: Re: mysqli rollback todos los insert si uno falla
Publicado por: Gallu en 11 Abril 2016, 11:24 AM
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";
}
Título: Re: mysqli rollback todos los insert si uno falla
Publicado por: gAb1 en 12 Abril 2016, 02:35 AM
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  :-\