Can you use mysqli prepared statements and transactions together? - php

All I want to know is if you can use mysqli's prepare, execute, and rollback together?
$m = new mysqli($dbhost,$dbuser,$dbpassword,$dbname);
$m->autocommit(FALSE);
$stmt = $m->prepare("INSERT `table` (`name`,`gender`,`age`) VALUES (?,?,?)");
$stmt->bind_param("ssi", $name, $gender, $age);
$query_ok = $stmt->execute();
$stmt = $m->prepare("INSERT `table` (`name`,`gender`,`age`) VALUES (?,?,?)");
$stmt->bind_param("ssi", $name, $gender, $age);
if ($query_ok) {$query_ok = $stmt->execute();}
if (!$query_ok) {$m->rollback();} else {$m->commit();}
Can you do this? Let's assume that the above code has a loop and or the variables get new data in them.

Best way to handle this is with exceptions (as always, darn PHP error/warning stuff). Simply because our commit() call may fail too. Note that finally is only available in newer PHP versions.
<?php
// Transform all errors to exceptions!
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$connection = new \mysqli($dbhost, $dbuser, $dbpassword, $dbname);
$connection->autocommit(false);
$stmt = $connection->prepare("INSERT `table` (`name`, `gender`, `age`) VALUES (?, ?, ?)");
$stmt->bind_param("ssi", $name, $gender, $age);
$stmt->execute();
// We can simply reuse the prepared statement if it's the same query.
//$stmt = $connection->prepare("INSERT `table` (`name`, `gender`, `age`) VALUES (?, ?, ?)");
// We can even reuse the bound parameters.
//$stmt->bind_param("ssi", $name, $gender, $age);
// Yet it would be better to write it like this:
/*
$stmt = $connection->prepare("INSERT `table` (`name`, `gender`, `age`) VALUES (?, ?, ?), (?, ?, ?)");
$stmt->bind_param("ssissi", $name, $gender, $age, $name, $gender, $age);
*/
$stmt->execute();
$connection->commit();
}
catch (\mysqli_sql_exception $exception) {
$connection->rollback();
throw $exception;
}
finally {
isset($stmt) && $stmt->close();
$connection->autocommit(true);
}

Related

How to run multiple INSERT queries in a transaction and use insert id?

I need to insert data into 3 tables and need to get the id of last inserted query into shopper table. I know this is doable by running
$conn -> insert_id;
in a single query but in my case I need to create a transaction with rollback in case of any failure. something like
$conn = new mysqli(DBHOST, DBUSER, DBPASS, DBNAME);
$stmt1 = $conn->prepare("INSERT INTO shopper (usersID, parentJob, phoneNumber,address) VALUES (?, ?, ?, ?)");
$stmt1->bind_param("ssss", $userId, $parentJob, $phoneB, $addressB);
$stmt2 = $conn->prepare("INSERT INTO shipment (shipmentID, usersID,..) VALUES (?, ?, ?, ?)");
$stmt2->bind_param("ssss", $userId, ...);
$stmt3 = $conn->prepare("INSERT INTO address (addressID, usersID, ...) VALUES (?, ?, ?, ?)");
$stmt3->bind_param("ss", $userId, ...);
$conn->begin_transaction();
if ($stmt1->execute() && $stmt2->execute() && $stmt3->execute()) {
$conn->commit();
} else {
$conn->rollback();
}
$conn->close();
As you can see I am trying to pass last inserted usersID as Foreign Key into shipment and address tables. so how can I do this when committing all of them together like
if ($stmt1->execute() && $stmt2->execute() && $stmt3->execute()) {
$conn->commit();
} else {
$conn->rollback();
}
Exceptions offer enormous help with transactions. Hence configure mysqli to throw exceptions. Not only for transactions but because it's the only proper way to report errors in general.
With exceptions your code will be plain and simple
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$conn = new mysqli(DBHOST, DBUSER, DBPASS, DBNAME);
$conn->set_charset('utf8mb4');
$conn->begin_transaction();
$stmt = $conn->prepare("INSERT INTO shopper (usersID, parentJob, phoneNumber,address) VALUES (null, ?, ?, ?)");
$stmt->bind_param("sss", $parentJob, $phoneB, $addressB);
$stmt->execute();
$userId = $conn->insert_id;
$stmt = $conn->prepare("INSERT INTO shipment (shipmentID, usersID,..) VALUES (?, ?, ?, ?)");
$stmt->bind_param("ssss", $userId, ...);
$stmt->execute();
$stmt = $conn->prepare("INSERT INTO address (addressID, usersID, ...) VALUES (?, ?, ?, ?)");
$stmt->bind_param("ss", $userId, ...);
$stmt->execute();
$conn->commit();
in case of error an exception will be thrown and a transaction will be rolled back automatically.

Mysqli bind parameters not working

I'm trying to use prepared statements to enter data in a database. The unprepared statement works but this prepared statement does not. I can't find out why.
Prepared version:
$stmt = $mysqli->prepare("INSERT INTO videos (file_name, upload_by, date, path)
VALUES (?, ?, ?, ?)");
$stmt->bind_param('ssss', $newstring, $id, $date->format('Y-m-d'), $location);
$stmt->execute();
Unprepared version:
$sql = "INSERT INTO videos (file_name, upload_by, date, path) VALUES ('$newstring', '$id', '
$date', 'Nominator/$location$newstring')";
mysqli_query($mysqli, $sql);
Replace $stmt-execute(); with $stmt->execute();
Also, don't use date and path in query. Rename them with some other name like date1 and path1.
Update your Query like below that will surely work (Tested Offline):
<?php
$mysqli = new mysqli('localhost', 'root', '', 'test2');
if ($mysqli->errno) {
printf("Connect failed: %s\n", $mysqli->error);
exit();
}
$stmt = $mysqli->prepare("INSERT INTO videos (file_name, upload_by, date1, path1) VALUES (?, ?, ?, ?)");
$stmt->bind_param('ssss', $file_name, $upload_by, $date1, $path1);
$date1 = date("Y-m-d");
$file_name = "test.jpg";
$upload_by = "amit";
$path1 = "test";
if ($result = $stmt->execute()){
echo "success";
$stmt->free_result();
} else {
echo "error";
}
$stmt->close();
?>
You are binding your parameter twice, if you are using only ?, don't bind parameter again just execute directly.
//Prepare your query first
$stmt = $mysqli->prepare("INSERT INTO videos (file_name, upload_by, date, path)
VALUES (?, ?, ?, ?)");
//Just pass your argument and execute directly without binding the parameter (The parameter is binded already)
$stmt->execute('ssss', $newstring, $id, $date->format('Y-m-d'), $location);

How to Use prepare statement to insert data to three different tables in MySQLi with consideration of transaction

What is the perfect and safest way to execute the following SQL statements simultaneously, with consideration of transaction in MySQLi in order the data to be added to all tables or the data needs to be rolled back when a failure happens to the adding process of one on the tables.
$conn = new mysqli(DBHOST, DBUSER, DBPASS, DBNAME);
$stmt1 = $conn->prepare("INSERT INTO stdHouseholder (usersID, parentJob, phoneNumber,address) VALUES (?, ?, ?, ?)");
$stmt1->bind_param("ssss", $userId, $parentJob, $phoneB, $addressB);
$stmt2 = $conn->prepare("INSERT INTO stdConfirmInfo (usersID, commitment, credentials, haveOfficialLetter) VALUES (?, ?, ?, ?)");
$stmt2->bind_param("ssss", $userId, $commitment, $credentials, $NamesEnglish);
$stmt3 = $conn->prepare("INSERT INTO users_roleTB (usersID, role_id) VALUES (?, ?)");
$stmt3->bind_param("ss", $userId, $role_id);
You can use the begin transaction, commit and rollback features of the mysqli commands to assist with you.
You'll want to start a transaction, check the result of each insert query and then commit (if they all performed well) or rollback if they didn't:
<?php
$conn = new mysqli(DBHOST, DBUSER, DBPASS, DBNAME);
$stmt1 = $conn->prepare("INSERT INTO stdHouseholder (usersID, parentJob, phoneNumber,address) VALUES (?, ?, ?, ?)");
$stmt1->bind_param("ssss", $userId, $parentJob, $phoneB, $addressB);
$stmt2 = $conn->prepare("INSERT INTO stdConfirmInfo (usersID, commitment, credentials, haveOfficialLetter) VALUES (?, ?, ?, ?)");
$stmt2->bind_param("ssss", $userId, $commitment, $credentials, $NamesEnglish);
$stmt3 = $conn->prepare("INSERT INTO users_roleTB (usersID, role_id) VALUES (?, ?)");
$stmt3->bind_param("ss", $userId, $role_id);
$conn->begin_transaction();
if ($stmt1->execute() && $stmt2->execute() && $stmt3->execute()) {
$conn->commit();
} else {
$conn->rollback();
}
$conn->close();
You can't insert to multiple tables in one statement
you will have to put them in a transaction
$conn = new mysqli(DBHOST, DBUSER, DBPASS, DBNAME);
$conn->query("BEGIN;");
$failed = false;
$stmt1 = $conn->prepare("INSERT INTO stdHouseholder (usersID, parentJob, phoneNumber,address) VALUES (?, ?, ?, ?)");
$stmt1->bind_param("ssss", $userId, $parentJob, $phoneB, $addressB);
if (!$stmt2->execute()) {
$failed = true;
$conn->query("ROLLBACK;");
}
if(!$failed){
$stmt2 = $conn->prepare("INSERT INTO stdConfirmInfo (usersID, commitment, credentials, haveOfficialLetter) VALUES (?, ?, ?, ?)");
$stmt2->bind_param("ssss", $userId, $commitment, $credentials, $NamesEnglish);
if (!$stmt2->execute()) {
$failed = true;
$conn->query("ROLLBACK;");
}
}
if(!$failed){
$stmt3 = $conn->prepare("INSERT INTO users_roleTB (usersID, role_id) VALUES (?, ?)");
$stmt3->bind_param("ss", $userId, $role_id);
if (!$stmt3->execute()) {
$failed = true;
$conn->query("ROLLBACK;");
}
}
if(!$failed){
$conn->query("COMMIT;");
}
LOCKING TABLES ?
If you want to make a transaction while locking your tables , you are going to need another approach ,because locking a table is going to commit any running transaction, and this is horrifying.
In this case you are going to start the transaction by turning off the auto commit feature of mysql
SET autocommit=0;
Then you lock your tables
LOCK TABLES stdHouseholder WRITE, stdConfirmInfo WRITE, users_roleTB WRITE;
Then run your prepared statements normally
$stmt->execute();
Finally, if the statements succeed then you commit the transaction , and turn the auto commit on again.
$conn->query("COMMIT;");
$conn->query("SET autocommit=1;");
Note that if you didn't commit (and didn't roll back) the transaction will be rolled-back when the session ends (but this is not guaranteed).

Mysqli to PDO binding parameters

I am switching from mysqli syntax to PDO and having some doubts:
Before I used this (example of binding int, string, decimal values):
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, id, value) VALUES (?, ?, ?)");
$stmt->bind_param("sid", $firstname, $id, $value);
$stmt->execute();
With PDO I should use this: (here param decimal already doesnt exist, not to mention that I have to write multiple lines for binging)
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, id, value) VALUES (?, ?, ?)");
$stmt->bindParam(1, $firstname, PDO::PARAM_STR);
$stmt->bindParam(2, $id, PDO::PARAM_INT);
$stmt->bindParam(3, $value, PDO::PARAM_STR);//no decimal type!
$stmt->execute();
Should I just 'forget' about types and do this?
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, id, value) VALUES (?, ?, ?)");
$stmt->execute([$firstname, $id, $value]);
How can int and decimal fail in this situation?
Yes, most of time you should.
There are only few extremely rare cases where you would have to fall bфck to separate binding. While for the both INT and DECIMAL string binding is all right.
Note that for the decimal type you should be using "s" in mysqli as well.

PHP SQL insert prepared statements doesn't insert properly

When executing the following code, no Errors occur but the data isn't put into the database!
$zero = 0;
$connection = new mysqli("localhost", "andrewle_me", "*****", "andrewle_velocity");
$stmt = $connection->prepare("INSERT INTO accounts Values(?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param("issssssi", $zero, $_POST["username"], password_hash($_POST["password"], PASSWORD_DEFAULT), $_POST["Email"], $_POST["firstname"], $_POST["lastname"], $_POST["nationality"], $zero);
$stmt->execute();
$stmt->close();
$connection->close();
echo "Success";
Define your posts and password hash outside of the param binding. Set the fields in the table that your values are going to be entered into.

Categories