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:
$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.
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:
$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";
}
Ah, al fin algo nuevo. Si que son muy utiles esos blockes. Gracias, dejo como lo he hecho:
$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 :-\