Possible to use multiple/nested MySQLi statements? - php

Is it possible to have a MySQLi prepared statement within the fetch() call of a previous statement? If not, what's the best way around it?
Example code:
if($stmt = $link->prepare("SELECT item FROM data WHERE id = ?")) {
$stmt->bind_param("i", $id);
$stmt->execute();
$stmt->bind_result($item);
while( $stmt->fetch() ) {
/* Other code here */
$itemSummary = $item + $magic;
if($stmt2 = $link->prepare("INSERT INTO summaries (itemID, summary) VALUES (?, ?)")) {
$stmt2->bind_param("is", $itemID, $itemSummary);
$stmt2->execute();
$stmt2->close();
}
}
}

This is the single connection way:
if($stmt = $link->prepare("SELECT item FROM data WHERE id = ?")) {
$stmt->bind_param("i", $id);
$stmt->execute();
$stmt->store_result(); // <-- this
$stmt->bind_result($item);
while( $stmt->fetch() ) {
/* Other code here */
$itemSummary = $item + $magic;
if($stmt2 = $link->prepare("INSERT INTO summaries (itemID, summary) VALUES (?, ?)")) {
$stmt2->bind_param("is", $itemID, $itemSummary);
$stmt2->execute();
$stmt2->store_result(); // <-- this
/*DO WHATEVER WITH STMT2*/
$stmt2->close();
}
}
}

You should be able to do that, although you make have to start a second connection.

Or use store_result.

Related

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

Mysqli INSERT command followed by an UPDATE

I would like to have data inserted in one table, and data updated in another through prepared statements in mysqli. Trying the following only executes the INSERT command:
EDITED:
if($stmt=$mysqli->prepare("SELECT bids_id, bid, fruit_volume FROM basket ORDER BY bid DESC")) {
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($bids_id, $bid, $fruit_volume);
while($stmt->fetch()) {
$stack = array($bids_id, $bid, $fruit_volume);
array_push($all_fruits, $stack);
}
$stmt->free_result();
}
foreach ($all_fruits as $fruits) {
if ($_POST["offer"] == $fruits[1] && $volume < $fruits[2]) {
$stmt2 = $mysqli->prepare("INSERT INTO oranges (username, price, volume, date) VALUES (?, ?, ?, ?)");
$stmt2->bind_param('sdis', $user, $price, $volume, $today);
$stmt2->execute();
$stmt3 = $mysqli->prepare("UPDATE basket SET fruit_volume = ? WHERE bids_id = ?");
$stmt3->bind_param('ii', 800, 1);
$stmt3->execute();
}
}
$mysqli->close();
bind_param passes by reference not by value,so you need to have those values in variables before they can be referenced
$a=800;
$b=1;
foreach ($all_fruits as $fruits) {
if ($_POST["offer"] == $fruits[1] && $volume < $fruits[2]) {
$stmt2 = $mysqli->prepare("INSERT INTO oranges (username, price, volume, date) VALUES (?, ?, ?, ?)");
$stmt2->bind_param('sdis', $user, $price, $volume, $today);
$stmt2->execute();
$stmt3 = $mysqli->prepare("UPDATE basket SET fruit_volume = ? WHERE bids_id = ?");
$stmt3->bind_param('ii',$a, $b);
$stmt3->execute();
}
}
Try this instead
foreach ($all_fruits as $fruits) {
if ($_POST["offer"] == $fruits[1] && $volume < $fruits[2]) {
$stmt2 = $mysqli->prepare("INSERT INTO oranges (username, price, volume, date) VALUES (?, ?, ?, ?)");
$stmt2->bind_param('sdis', $user, $price, $volume, $today);
$stmt2->execute();
$stmt3 = $mysqli->prepare("UPDATE basket SET fruit_volume = ? WHERE bids_id = ?");
$stmt3->bind_param('ii', 800, 1);
$stmt3->execute();
}
}
$mysqli->close();

Return the id (auto increment) of an inserted line

I insert data into a table called 'roster'. The first column (id_roster) is an id using mysql auto-increment.
I run a SELECT to find the id_roster
I use this id_roster to insert it into a table 'roster_par_membre' along with other data
if ($insert_stmt = $mysqli->prepare("INSERT INTO `roster`(`nom_roster`, `description_roster`, `id_organisation`, `created_by`, `creation_date`,`modified_by`) VALUES (?, ?, ?, ?, ?, ?)")) {
$insert_stmt->bind_param('ssiisi', $roster_name, $description_roster, $organisation_id, $user_id, $creation_date, $user_id);
if (!$insert_stmt->execute()) {
$reponse = 'Sorry, a database error occurred; please try later';
} else {
// if INSERT OK -> create a new line in roster_membre table
//1. get the roster_id
$sql = "SELECT r.id_roster
FROM roster r
WHERE r.nom_roster = ?
LIMIT 1";
$stmt = $mysqli->prepare($sql);
if ($stmt) {
$stmt->bind_param('s', $roster_name);
$stmt->execute(); // Execute the prepared query.
$stmt->store_result();
$stmt->bind_result($id_roster);
$stmt->fetch();
$level = 1;
//2. create a line with the roster_id and insert the membre as level 1
$insert_stmt = $mysqli->prepare("INSERT INTO `roster_par_membre`(`id_membre`, `id_roster`, `level`, `modified_by`) VALUES (?,?,?,?)");
$insert_stmt->bind_param('iiii', $user_id, $id_roster, $level, $user_id);
$insert_stmt->execute();
$reponse = 'success';
}
So far the code is working but it is not very nice.
Is there a way when we create a new line in a table to directly return a value (id with auto-increment) to be used in a sql query (to insert data into a second table)? or maybe to merge the two query (the two INSERT) in one statment?
short edit: it is an AJAX $response the return value (JSON)
Ok,solution:
//1. get the roster_id
$sql = "SELECT r.id_roster
FROM roster r
WHERE r.nom_roster = ?
LIMIT 1";
$stmt = $mysqli->prepare($sql);
if ($stmt) {
$stmt->bind_param('s', $roster_name);
$stmt->execute(); // Execute the prepared query.
$stmt->store_result();
$stmt->bind_result($id_roster);
$stmt->fetch();
Just need to replace all this part by
$id_roster = $mysqli->insert_id;
nice and easy. THANKS to albanx
these are the functions I used for query on projects that I do not want to use any framework (just php):
/**
*
* Executes query methods
* #param string $query the query string
* #param array $vals array of values
* #param bool $show show the query
* #return int/array/false
*/
function q($query, $vals=array(), $show_query=false)
{
$conn = new mysqli(...)
$offset = 0;
foreach ($vals as $v)
{
$cv = $conn->real_escape_string($v);//escape the value for avoiding sql injection
$fv = ($v===NULL) ? 'NULL':"'".$cv."'"; //if value is null then insert NULL in db
$qpos = strpos($query, '?', $offset);//replace the ? with the valeue
$query = substr($query, 0, $qpos).$fv.substr($query, $qpos+1);
$offset = $qpos+strlen($cv)+1;
}
$result = $conn->query($query);
if($show || $result===false) echo $query."<br>";
$rows = array();
if($result===true)
{
return $conn->affected_rows;
}
else if($result===false)
{
return false;
}
else
{
while ($row = $result->fetch_array(MYSQLI_ASSOC) )
{
$rows[]=$row;
}
}
return $rows;
}
function lastid()
{
return $this->qval("SELECT LAST_INSERT_ID()");
}
Usage example:
q('INSERT INTO USER(name, email) VALUES(?,?)', array('admin','admin#admin.com'));
$id = lastid();

adding error msg to prepared statements

I'm trying to convert mysql_query over to prepared statements, but it's failing silently and I'm not sure where I'm going wrong. Here's my proc.php page for a form:
$db = new PDO('mysql:host=XXX;dbname=XXX;charset=utf8', 'XXX', 'XXX');
if ($_POST['submit']) {
$type = $_POST['type'];
$auth1_lname = trim($_POST['auth1_lname']);
$auth1_fname = trim($_POST['auth1_fname']);
$today = date("Y-m-d");
$stmt = $db->prepare("INSERT INTO table_base ( type , publ_date , auth1_lname , auth1_fname )
VALUES (:type, :today, :auth1_lname , :auth1_fname) ");
$stmt->bindParam(':type', $type);
$stmt->bindParam(':today', $today);
$stmt->bindParam(':auth1_lname', $auth1_lname);
$stmt->bindParam(':auth1_fname', $auth1_fname);
$stmt->execute();
$bid = $db->lastInsertId();
$subj_area = $_POST['subj_area'];
$subject = 'subj_area';
$subjs = '';
$stmt = $db->prepare("INSERT INTO table_meta (bid, key, value) VALUES (:bid, :key, :value)");
$stmt->bindParam(':bid', $bid);
$stmt->bindParam(':key', $subject);
$stmt->bindParam(':value', $subjs, PDO::PARAM_STR);
foreach($subj_area as $subjs) {
$stmt->execute();
}
$geo_area = $_POST['geo_area'];
$geograph = 'geo_area';
$geos = '';
$stmt = $db->prepare("INSERT INTO table_meta (bid, key, value) VALUES (:bid, :key, :value)");
$stmt->bindParam(':bid', $bid);
$stmt->bindParam(':key', $geograph);
$stmt->bindParam(':value', $geos, PDO::PARAM_STR);
foreach($geo_area as $geos) {
$stmt->execute();
}
}
I'm not sure I'm even doing this right.
I see comments elsewhere on SO that your PHP must be this tall to use PDO, but php.net's page on PDO doesn't list PHP requirements. Am I failing b/c my PHP5 host doesn't have the right drivers?
Is there a way to add a die(mysql_error()) so at least it wouldn't be a silent failure?

Invalid parameter number on PDO Prepared Statement

I'm working with a sequence of queries created with PDO class, in some case, my queries needs the same parameter.
I've created an array used in a foreach statement which save the data but some variables come from outside, can I use both data in one query?
the example:
// $connection is the PDO object;
// $full_data contains:
// $full_data[$i]["address"]
// $full_data[$i]["phone"]
// $full_data[$i]["email"]
// $full_data[$i]["user_id"]
// $full_data[$i]["surname"] // not used but present
// $full_data[$i]["name"] // not used but present
$sql = "UPDATE users_table SET city = :address, phone = :phone, email = :email, admin_id = :admin_id, admin_name = :admin_name WHERE user_id = :user_id";
$statement = $connection->prepare ($sql);
$statement->bindParam (':admin_id', trim($admin_id), PDO::PARAM_INT);
$statement->bindParam (':admin_name', trim($admin_name), PDO::PARAM_STR);
foreach ($full_data as $value) {
$ok = $statement->execute ($value);
$num = $statement->rowCount ();
}
} catch (PDOException $e) {
return $e->getMessage ();
}
this page return me the error:
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
what is exactly the problem, on an UPDATE statement the technique works
damn, I've found the problem after hours...
// $connection is the PDO object;
// $full_data contains:
// $full_data[$i]["address"]
// $full_data[$i]["phone"]
// $full_data[$i]["email"]
// $full_data[$i]["user_id"]
// ==> $full_data[$i]["surname"] // not used but present
// ==> $full_data[$i]["name"] // not used but present
the array data not saved in the query ["surname"] and ["name"] generate the error.
It seems like execute (); needs precise array data structure.
I've solved the problem by using this:
$sql = "UPDATE users_table SET city = :address, phone = :phone, email = :email, admin_id = :admin_id, admin_name = :admin_name WHERE user_id = :user_id";
$statement = $connection->prepare ($sql);
// must be removed ==> $statement->bindParam (':admin_id', trim($admin_id), PDO::PARAM_INT);
// must be removed ==> $statement->bindParam (':admin_name', trim($admin_name), PDO::PARAM_STR);
for ($i = 0; $i < count($full_data); $i++) {
$full_data[$i]["admin_name"] = "the admin name";
$full_data[$i]["admin_id"] = "100";
unset ($full_data[$i]["surname"]); // IMPORTANT: must remove the unused vars
unset ($full_data[$i]["name"]); // IMPORTANT: must remove the unused vars
}
foreach ($full_data as $value) {
// bindParam can be avoided, but it's recommended for data type security
$statement->bindParam(':address', trim($value['address']), PDO::PARAM_STR);
$statement->bindParam(':phone', trim($value['phone']), PDO::PARAM_STR);
$statement->bindParam(':email', trim($value['email']), PDO::PARAM_STR);
$statement->bindParam(':admin_id', trim($value['admin_id']), PDO::PARAM_INT);
$statement->bindParam(':admin_name', trim($value['admin_name']), PDO::PARAM_STR);
$ok = $statement->execute ($value);
$num = $statement->rowCount ();
}
} catch (PDOException $e) {
return $e->getMessage ();
}
You need to bind the :address, :phone, and :email parameters.
To elaborate on BD answer you're missing the following lines of code:
$statement->bindParam (':address', trim($address), PDO::PARAM_STR);
$statement->bindParam (':phone', trim($phone), PDO::PARAM_STR);
$statement->bindParam (':email', trim($email), PDO::PARAM_STR);
Plus, something seems to be wrong with your foreach loop, I think this is what you want:
$sql = "UPDATE users_table SET city = :address, phone = :phone, email = :email, admin_id = :admin_id, admin_name = :admin_name";
$statement = $connection->prepare($sql);
$statement->bindParam(':admin_id', trim($admin_id), PDO::PARAM_INT);
$statement->bindParam(':admin_name', trim($admin_name), PDO::PARAM_STR);
foreach ($full_data as $value)
{
$statement->bindParam(':address', trim($value['address']), PDO::PARAM_STR);
$statement->bindParam(':phone', trim($value['phone']), PDO::PARAM_STR);
$statement->bindParam(':email', trim($value['email']), PDO::PARAM_STR);
$ok = $statement->execute();
$num = $statement->rowCount();
}

Categories