Right way to check an database insertion and rollback with PDO - php

I have here a code to insert the order of the customer in the orders table and insert the purchased products in that order in the purchased_products table. I want to check if the insertions were made, otherwise undo the changes with PDO rollback(). My code is:
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
];
try
{
$connection = new PDO("mysql:host={$HOST};dbname={$DB_NAME}", $USERNAME, $PASS, $options);
}
$connection->beginTransaction();
try
{
$sql = "INSERT INTO orders (customer_id, customer_name, order_value, order_date)
VALUES (?, ?, ?, ?)";
$query = $connection->prepare($sql);
$query->execute(array
(
$user_id,
$user['user_name'],
$order_value,
$date
));
$id_of_respective_order = $connection->lastInsertId();
}
catch(PDOException $exception)
{
$connection->rollback();
echo "<script>alert('An error occurred while completing your purchase. Please try again later.');</script>";
}
try
{
$sql = "INSERT INTO purchased_products (order_id, product_name, product_price, quantity)
VALUES (?, ?, ?, ?)";
$query = $connection->prepare($sql);
foreach($_SESSION['cart'] as $product)
{
$query->execute(array
(
$id_of_respective_order,
$product['product_name'],
$product['product_price'],
$product['quantity']
));
}
}
catch(PDOException $exception)
{
$connection->rollback();
echo "<script>alert('An error occurred while completing your purchase. Please try again later.');</script>";
}
$connection->commit();
Is this way safe? I use a transaction to lock the tables and lastInsertId () to assign the ID of the order to the products that belongs to it. I check the insertions and if something went wrong undo the changes with rollback(). Is my checkout system well prepared and totally safe?

It makes more sence to do all your inserts inside the same Try/Catch and then if the order insert or the order_item insert fails a single catch block will deal with the rollback and any cleanup/reporting that may be required.
The way you had it the order insert could fail and then the order_item insert would still try and run, possibly creating items without a owning order.
try {
$connection = new PDO("mysql:host={$HOST};dbname={$DB_NAME}",
$USERNAME, $PASS);
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch (PDOException $e ) {
echo 'Connection failed: ' . $e->getMessage();
exit;
}
$connection->beginTransaction();
try {
$sql = "INSERT INTO orders
(customer_id, customer_name, order_value, order_date)
VALUES (?, ?, ?, ?)";
$query = $connection->prepare($sql);
$query->execute(array( $user_id,
$user['user_name'],
$order_value,
$date
)
);
$id_of_respective_order = $connection->lastInsertId();
$sql = "INSERT INTO purchased_products
(order_id, product_name, product_price, quantity)
VALUES (?, ?, ?, ?)";
$query = $connection->prepare($sql);
foreach($_SESSION['cart'] as $product) {
$query->execute(array( $id_of_respective_order,
$product['product_name'],
$product['product_price'],
$product['quantity']
)
);
}
$connection->commit();
}
catch(PDOException $e) {
$connection->rollBack();
echo 'Order creation failed: ' . $e->getMessage();
echo "<script>alert('An error occurred while completing your purchase. Please try again later.');</script>";
exit;
}

Related

update quantity of product after checkout

I am trying to update the item quantity after the finished transaction. I have quantity on my item and I wanted to lessen the item quantity depending on the item ordered. the transaction works fine but the problem is the update does not execute and it does not update the quantity of the item in my table.
the code:
<?php
include 'includes/session.php';
if(isset($_GET['confirmation'])){
$confirmation = $_GET['confirmation'];
$p_quantity = $_GET['p_quantity'];
$date = date('Y-m-d');
$conn = $pdo->open();
try{
$stmt = $conn->prepare("INSERT INTO sales (user_id, confirmation, status, sales_date) VALUES (:user_id, :confirmation, :status, :sales_date)");
$stmt->execute(['user_id'=>$user['id'], 'confirmation'=>$confirmation, 'status'=>1, 'sales_date'=>$date]);
$salesid = $conn->lastInsertId();
$stmt = $conn->prepare("UPDATE products SET p_quantity=p_quantity-:p_quantity WHERE id=:id");
$stmt->execute(['p_quantity'=>$p_quantity, 'id'=>$id]);
try{
$stmt = $conn->prepare("SELECT * FROM cart LEFT JOIN products ON products.id=cart.product_id WHERE user_id=:user_id");
$stmt->execute(['user_id'=>$user['id']]);
foreach($stmt as $row){
$stmt = $conn->prepare("INSERT INTO details (sales_id, product_id, quantity) VALUES (:sales_id, :product_id, :quantity)");
$stmt->execute(['sales_id'=>$salesid, 'product_id'=>$row['product_id'], 'quantity'=>$row['quantity']]);
}
$stmt = $conn->prepare("DELETE FROM cart WHERE user_id=:user_id");
$stmt->execute(['user_id'=>$user['id']]);
$_SESSION['success'] = 'Transaction successful. Thank you.';
}
catch(PDOException $e){
$_SESSION['error'] = $e->getMessage();
}
}
catch(PDOException $e){
$_SESSION['error'] = $e->getMessage();
}
$pdo->close();
}
header('location: profile.php');
?>

getting error for mysql when i am using if else in there

getting error for mysql when i am using if else in there. i dont know what should i do and when i am using duplicate condition to update then it not woring i am not be able to find where is error
this is the error which is i am getting.
ERROR:SQLSTATE[HY093]: Invalid parameter number: parameter was not
defined
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt=$conn->prepare("SELECT uniqueid FROM hotelcarttemp WHERE uniqueid=:uniqueid");
$stmt->execute(array(':uniqueid'=>$uniqueid));
$count=$stmt1->rowCount();
echo "count-".$count;
if($count>0)
{
$sql = "UPDATE hotelcarttemp SET `hotelname`='".$hotelname."',`roomtype`='".$roomtype."',`checkin`='".$checkin."',`checkout`='".$checkout."',`Country`='".$Country."',`Destination`='".$Destination."',`price`='".$price."' WHERE uniqueid='".$uniqueid."'";
echo "sql- ".print_r($sql);
$stmt = $conn->prepare($sql);
// echo print_r($stmt);
$stmt->execute();
}
else
{
$sql = "INSERT INTO hotelcarttemp (timestamp, packageid, uniqueid, hotelname, roomtype, checkin, checkout, Country, Destination, hoteldetail, price)
VALUES ('"
.$timestamp."','"
.$packageid."','"
.$uniqueid."','"
.$hotelname."','"
.$roomtype."','"
.$checkin."','"
.$checkout."','"
.$Country."','"
.$Destination."','"
.addslashes($hoteldetail)."','"
.$price."'
)";
// echo "sql- ".print_r($sql);
$stmt = $conn->prepare($sql);
// echo print_r($stmt);
$stmt->execute();
}
}
catch(PDOException $e) {
echo 'ERROR:' . $e->getMessage();
} here
Your SELECT query where condition is WHERE uniqueid=:uniqueid
And you are binding username to it
$stmt->execute(array(':username'=>$uniqueid));//:username invalid parameter
Change this to
$stmt->execute(array(':uniqueid'=>$uniqueid));

SQL Query insertion check inside transaction with rollback

In my shop system I'm using this code to insert in the DB customer order details and the products that belongs to that o:
$connection->beginTransaction();
try
{
$sql = "INSERT INTO orders (customer_id, order_price, order_date, order_hour)
VALUES (?, ?, ?, ?)";
$query = $connection->prepare($sql);
$query->execute(array
(
$user['user_id'],
$order_price,
$date,
$hour
));
if($query)
{
$id_of_respective_order = $connection->lastInsertId();
$sql = "INSERT INTO purchased_products (order_id, product_name, product_price, quantity, extras)
VALUES (?, ?, ?, ?, ?)";
$query = $connection->prepare($sql);
foreach($_SESSION['cart'] as $product)
{
$extras = null;
$product_price = $product['product_price'] * $product['quantity'];
if($product['extras'] != NULL)
{
foreach($product['extras'] as $extra)
{
$extras .= $extra['extra_quantity'] ."x". $extra['extra_name'] ."<br/>";
$product_price += $extra['extra_total'] * $product['quantity'];
}
}
$query->execute(array
(
$id_of_respective_order,
$product['product_name'],
$product_price,
$product['quantity'],
$extras
));
}
unset($_SESSION['cart']);
echo "<script>alert('Your purchase was completed!');
window.location = '/my-orders.php';
</script>";
}
else
{
echo "<script>alert('An error ocurred while completing your purchase. Please try again!');
window.location = '/my-cart.php';</script>";
}
$connection->commit();
}
catch(PDOException $exception)
{
$connection->rollBack();
echo "<script>alert('An error ocurred while completing your purchase. Please try again!');
window.location = '/my-cart.php';</script>";
}
My question is in regards to code optimization for error checking.
It is recommended that I use if ($query) even with the catch and rollBack as I'm doing? It is necessary or I can use only catch and rollBack because it will check for erros by itself?
You don't need to use if if you have your PDO error set to throwing the exception.
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

PHP Prepared Statements handle duplicate entry

I have a problem, I am not able to handle a duplicate entry with prepared statements.
I want to end the program when a duplicate entry appears. This is what I have been trying to do:
function insert_vulnerability ($CVE, $Description, $Date, $Score, $Type){
$conn = connection();
$stmt = $conn->prepare("INSERT INTO Vulnerabilities (CVE, Description, Date, Score, Type)
VALUES (?, ?, ?, ?, ?)");
$stmt->bind_param("sssis", $CVE, $Description, $Date, $Score, $Type);
if ( false === $stmt ) {
die('prepare() failed: ' . htmlspecialchars($mysqli->error));
}
$stmt->execute();
$conn->close();
}
When not using prepared statements I handled the error this way and everything worked perfectly:
function insert_vulnerability ($CVE, $Description, $Date, $Score, $Type){
$conn = connection();
$Description = htmlspecialchars($Description);
$sql = "INSERT INTO Vulnerabilities (CVE, Description, Date, Score, Type)
VALUES ('".$CVE."', '".$Description."', '".$Date."', '".$Score."', '".$Type."')";
if ($conn->query($sql) === TRUE) {
//echo "New record created successfully";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
$conn->close();
die();
}
$conn->close();
}
So how do I get the same result with prepared statements ¿?
Thank you in advance.
You could just connect the checking inside the $stmt->execute() to see if the prepared statement did work properly.
function insert_vulnerability ($CVE, $Description, $Date, $Score, $Type){
$conn = connection();
$stmt = $conn->prepare('
INSERT INTO Vulnerabilities (CVE, Description, Date, Score, Type)
VALUES (?, ?, ?, ?, ?)
');
$stmt->bind_param('sssis', $CVE, $Description, $Date, $Score, $Type);
if($stmt->execute()) { // true, success, else error
echo 'New record created successfully';
} else {
echo $conn->error;
}
$conn->close();
}
Just to note, you have an undefined variable on your prepared statement side:
$mysqli->error

Is this bad practice for inserting data into db?

I have this function that inserts data from a checkbox into my sql database and it works just find, but Im pretty new to this so I would like to know if there is a better/safer (from sql injections) way to do this. I know I should be using PDO with prepared statements, but that is something I am tackling later.
Here is the form that produces the html checkboxes:
<form action="" method="post">
<?php
if(empty($clients) === true){
echo '<p>You do not have any clients yet.</p>';
}
else
{
foreach($clients as $client){
echo'
<input type="checkbox" name="client_data[]" value="'.$_SESSION['user_id'].'|'.$class_id.'|'.$client['first_name'].'|'.$client['nickname'].'|'.$client['last_name'].'">
'.$client['first_name'].' ('.$client['nickname'].') '.$client['last_name'].'
<br />';
} // foreach($client
} // if empty
?>
Here is the php that calls the function:
if (isset($_POST['exist_to_class'])){
if (empty($_POST['client_data']) === true){
$errors [] = 'You much select a client to be added to the class.';
} else {
if (isset($_POST['client_data']) && !empty($_POST['client_data']));
foreach ($_POST['client_data'] as $cd){
exist_client_to_class($cd);
header('Location: view_class.php?class_id='.$class_id.' ');
} // foreach $cd
} // else
} //isset
And here is my function that inserts the data into the db:
// add existing client to class ----------------------------------------------------
function exist_client_to_class($cd){
list($user_id, $class_id, $first_name, $last_name, $nickname) = explode('|', $cd);
mysql_query("INSERT INTO `clients` (user_id, class_id, first_name, last_name, nickname, date)
VALUES('$user_id', '$class_id', '$first_name', '$last_name', '$nickname', CURDATE())");
}
First stab at a PDO prepared statement:UPDATE
function exist_client_to_class($cd){
try{
$stmt = $conn->prepare('INSERT INTO clients
(user_id, class_id, first_name, last_name, nickname, date)
VALUES (:user_id, :class_id, :first_name, :last_name, :nickname, CURDATE())
');
list($user_id, $class_id, $first_name, $last_name, $nickname) = explode('|', $cd);
$stmt->execute(array(
':user_id' => $user_id,
':class_id' => $class_id,
':first_name' => $first_name,
':last_name' => $last_name,
':nickname' => $nickname
)
);
}
catch(PDOException $e) {
echo 'Error: ' . $e->getMessage();
}
}
Here is the db connect file:
//PDO database connect
try {
$conn = new PDO('mysql:host=localhost;dbname=customn7_cm', '**********', '**********');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn->exec("SET CHARACTER SET utf8");
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
To put simply, Yes.
You aren't sanitizing or escaping your user data in anyway. you are using the old mysql_* community deprecated functions. You're best bet is to start using PDO or Mysqli
Read this article: PHP Database Access: Are You Doing It Correctly?
This works for most sql injections: (from php.net)
decleration:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier = NULL ] )
// Connect
$link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password') OR die(mysql_error());
// Query
$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
mysql_real_escape_string($user),
mysql_real_escape_string($password));

Categories