Avoiding code repetition for MySQL queries (multiple while) - php

I have the following code:
<?php
include_once "connect.php";
$question_01 = mysqli_real_escape_string($con, $_POST['question_01']);
// $question_02 - $question_09 go here...
$question_10 = mysqli_real_escape_string($con, $_POST['question_10']);
$i = 0;
$array_sum=[];
while ($i < 10){
$i++;
$sql = "SELECT * FROM parteners WHERE question_no = 1 AND answer_variant = '$question_01'";
$result = mysqli_query($con, $sql);
$final_array_1 = array();
while ($row = mysqli_fetch_array($result, MYSQLI_NUM))
{
$final_array_1 = $row;
$array_sum = array_map(function () {
return array_sum(func_get_args());
}, $array_sum, $final_array_1);
}
}
print_r($final_array_1);
As you can see, I need to repeat the code for each $question_##. Is there a smarter way of doing this other than repeating the code? I'm not only concerned about turning everything into a code spaghetti but also about the efficiency of the operations as in loading times.
Let me know if you need clarification.
Update: Basically it should increase the value of "question_no" in the query until it reaches 10 and pick the corresponding $_POST value for each question.

There are two ways, variable variables or arrays. I'd suggest arrays as they are less prone to throwing errors everywhere.
<?php
include_once "connect.php";
$questions = array();
$questions[1] = mysqli_real_escape_string($con, $_POST['question_01']);
// $question_02 - $question_09 go here...
$questions[10] = mysqli_real_escape_string($con, $_POST['question_10']);
$i = 0;
$array_sum=[];
while ($i < 10){
$i++;
$sql = "SELECT * FROM parteners WHERE question_no = $i AND answer_variant = '".$questions[$i]."'";
$result = mysqli_query($con, $sql);
$final_array_1 = array();
while ($row = mysqli_fetch_array($result, MYSQLI_NUM))
{
$final_array_1 = $row;
$array_sum = array_map(function () {
return array_sum(func_get_args());
}, $array_sum, $final_array_1);
}
}
print_r($final_array_1);
EDIT: The reason I used an array instead of just straight up using the POST variable in the while loop is so there is room before you run anything for validation (ensuring your question array contains 10 posted values etc)

I would build one SQL-Statement which contains all questions and anwsers and do the rest with programming logic. SQL-Queries in a loop are a bad idea, because you have to do a lot of overhead for getting a task done, which the database server can do better. Also you should use prepared statements for performance and security.
$query = "SELECT * FROM parteners WHERE (question_no = 1 AND answer_variant = ?) OR (question_no = 2 AND answer_variant = ?) OR (question_no = 3 AND answer_variant = ?) OR (question_no = 4 AND answer_variant = ?) OR (question_no = 5 AND answer_variant = ?) OR (question_no = 6 AND answer_variant = ?) OR (question_no = 7 AND answer_variant = ?) OR (question_no = 8 AND answer_variant = ?) OR (question_no = 9 AND answer_variant = ?) OR (question_no = 10 AND answer_variant = ?)"
$stmt = myqli_prepare($query);
mysqli_stmt_bind_param($stmt, 'ssssssssss', $question_01, $question_02, $question_03,.....);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);

First, to make your code modern and efficient, you should be using PHP
Data Objects, or PDO for short. You will have access to prepared
statements, which are made exactly for this: you build a query
"template" and execute with different data, very efficiently and secure.
The loop is the proper way to do it. Also, your $questions array is a
bit unecessary since you can retrieve data from $_POST right inside
your loop. But if you want to use it, there is no need to "escape" the
string for the database, since it's handled by PDO. So you can build
your array in a easier way:
$questions = [
$_POST['question_01'],
$_POST['question_02'],
$_POST['question_03'],
# ...
$_POST['question_10'],
];
Your loop with PDO:
$dbh = ... # create your database handle, connect to it
$st = $dbh->prepare("
SELECT * FROM parteners
WHERE question_no = ? AND answer_variant = ?;
");
foreach (range(1, 10) as $i) {
$result = $st->execute([ $i, $questions[$i-1] ]);
# or, to build directly
$result = $st->execute([
$i, $_POST[ sprintf("question_%02d", $i) ]
]);
$final_array[] = $result->fetchAll(PDO::FETCH_NUM);
}
print_r($final_array);

Related

Collect values from JSON file for use in MySQL `IN` clause

include "includes/dbh.inc.php";
$data = file_get_contents("dumps/datadump-hung1.json");
$post = json_decode($data, true);
foreach($post['sessionResult']['leaderBoardLines'] as $userArr){
$carname .=$userArr['car']['carModel'];
} echo $carname;
this echoes out the carmodel numbers like this= 19115 instead of 19 1 15
when i try to match these with my database to echo out the name of cars the numbers respond to with:
$carGETdb = "SELECT carName FROM cars WHERE carID IN ($carname)";
$result = mysqli_query($conn, $carGETdb);
$row = mysqli_fetch_array($result)["carName"];
echo $row;
it posts nothing, because no cars is associated with 19115 but 3 different cars is associated with 19, 1 and 15
is there a way to foreach each array in singles, so i can match them with my database and echo out as carnames instead of numbers?
Map the values you're after to a new array
Build a prepared statement with the appropriate number of ? parameters in your IN clause
Bind the array of values to your statement
Execute and fetch
$carIds = array_map(function($userArr) {
return $userArr['car']['carModel'];
}, $post['sessionResult']['leaderBoardLines']);
// [ 19, 1, 15 ]
$placeholders = implode(', ', array_fill(0, count($carIds), '?'));
// "?, ?, ?"
$bindTypes = str_repeat('i', count($carIds));
// "iii"
$stmt = $conn->prepare(
"SELECT `carID`, `carName` FROM `cars` WHERE `carID` IN ($placeholders)");
$stmt->bind_param($bindTypes, ...$carIds);
$stmt->execute();
$stmt->bind_result($carId, $carName);
$carNames = [];
while ($stmt->fetch()) {
$carNames[$carId] = $carName;
}
var_dump($carNames);
Using your code...
include "includes/dbh.inc.php";
$data = file_get_contents("dumps/datadump-hung1.json");
$post = json_decode($data, true);
$carname = [];
foreach($post['sessionResult']['leaderBoardLines'] as $userArr){
$carname[] = $userArr['car']['carModel'];
}
$carnames = implode(',', $carname);
echo $carnames;
Then with your following statement when querying the database, you can use the implded value ( but this is bound to sql injection attacks ). You should use parameterised query instead. However, I am not going to change too much of your code so you can see what is going on.
$carGETdb = "SELECT carName FROM cars WHERE carID IN ($carnames)";
$result = mysqli_query($conn, $carGETdb);
$row = mysqli_fetch_array($result)["carName"];
echo $row;

How to combine multiple SQL SELECT results into one array in PHP?

Hello this code works but currently it is very repetitive.
I have been trying for a few days now, and I just cannot work out how to rewrite these separate SELECTs into one loop using arrays.
I would like to push about 20 entries, so if I cannot work out how to do the loop then its going to messy! :(
I would be grateful for any help with the first loop and from that I will try and solve the rest myself from that advice, Thanks..
// Create 6 random numbers from table
$ran_num = (RandomInRange(1,$total,6));
// Select data from table from a random row
$grab1 = "select * from stock_tbl where stock_id='$ran_num[0]'";
$grab2 = "select * from stock_tbl where stock_id='$ran_num[1]'";
$grab3 = "select * from stock_tbl where stock_id='$ran_num[2]'";
$grab4 = "select * from stock_tbl where stock_id='$ran_num[3]'";
$grab5 = "select * from stock_tbl where stock_id='$ran_num[4]'";
$grab6 = "select * from stock_tbl where stock_id='$ran_num[5]'";
$result1 = $mysqli->query($grab1);
$result2 = $mysqli->query($grab2);
$result3 = $mysqli->query($grab3);
$result4 = $mysqli->query($grab4);
$result5 = $mysqli->query($grab5);
$result6 = $mysqli->query($grab6);
// Convert result into an array called items
$item1 = mysqli_fetch_row($result1);
$item2 = mysqli_fetch_row($result2);
$item3 = mysqli_fetch_row($result3);
$item4 = mysqli_fetch_row($result4);
$item5 = mysqli_fetch_row($result5);
$item6 = mysqli_fetch_row($result6);
I managed to solve this with help on this thread..
I replaced all this code with:
// Create 6 random numbers from table
$ran_num = (RandomInRange(1,$total,6));
foreach ($ran_num as $key => $value) {
$grab[$key] = "select * from stock_tbl where stock_id='$value'";
$result = $mysqli->query($grab[$key]);
$item[$key] = mysqli_fetch_row($result);
}
Thank you very much :)
First of all, do you really need *?
What's wrong with using a single query with the IN() function? For example:
// Create 6 random numbers from table
$ran_num = (RandomInRange(1,$total,6));
// Select(grab) data from table from a random row
$grab = 'SELECT * FROM stock_tbl WHERE stock_id IN ('.implode(',', $ran_num).')';
<?php
$ran_num = (RandomInRange(1,$total,6));
foreach ($ran_num as $key => $value)
{
$grab[$value] = "select * from stock_tbl where stock_id='$value'";
}
?>
If you don't need to access each statement individually, this is also possible:
<?php
$ran_num = (RandomInRange(1,$total,6));
$grab = '';
foreach ($ran_num as $key => $value)
{
$grab .= "select * from stock_tbl where stock_id='$value';";
}
?>
That's essentially creating one long string that SQL will parse as individual commands using the semi-colon as the delimiter for each one.
You can concatenate strings in PHP with the '.' operator. Also, setting the grab variables as an array will make this easier as well, if that is possible in your implementation. You're probably looking for code such as:
for (int i=0; i < 20; i++)
$grab[i] = "select * from stock_tbl where stock_id='$ran_num[".i."]'";

What is the correct way of printing results set by `SELECT *` with multiple `WHERE` clauses in prepared statements using `fetch_assoc()` in PHP-MYSQL?

My query is below:
$query = 'SELECT * FROM `table_name` WHERE `uid` = ? AND `email` = ?';
$stmt_select = $conn->prepare($query);
$stmt_select->bind_param('is', $user_id, $user_email);
$user_id = $_SESSION['uid'];
$user_email = $_SESSION['user_email'];
$result = $stmt_select->execute();
I want to print results set using fetch_assoc, this is because I don't want to bind_result() to all 23 columns' name, I want to print it using $row['column_name'], what is the correct way of achieving it?
If you want to see my code, I asked another question here:
How to SELECT * with multiple WHERE using fetch_assoc in prepared statements in PHP-MYSQL?
But it didn't get answered correctly, so instead of correcting my code, can you tell me what's the best way to achieve it?
I had the same issue using SELECT * in mysqli. The issue lies with the bind_result part. Normally you would need to add many variables. Meaning if you had about 20 columns, you would need 20 variables to include in the bind_result statement.
I've tailored my solution to fix your issues.
$query = 'SELECT * FROM `table_name` WHERE `uid` = ? AND `email` = ?';
$stmt_select = $conn->prepare($query);
$stmt_select->bind_param('is', $user_id, $user_email);
$user_id = $_SESSION['uid'];
$user_email = $_SESSION['user_email'];
$result = $stmt_select->execute();
$stmt_select->store_result();//after this line we can output the number of rows if you need to check that as well
$number_of_rows = $stmt_select->num_rows;
$meta = $stmt_select ->result_metadata();
$parameters = array();
$results = array();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt_select , 'bind_result'), $parameters);
while ($stmt_select ->fetch()) {
foreach ($row as $key => $val) {
$x[$key] = $val; //here we get the key => value (Column => Value)
}
$results[] = $x; //lets grab everything here
}
print_r($results);

How can I put the id of the school_year?

My problem is when I save my student_votes like this:
I want that instead of the year will be save I would like to save the id. For an example, if today's year is 2016 then it will be save 2000(because it is the id of the year 2015). The first image is the studentvotes table,
it should be like syearid =2000 if the year is 2015 or if 2016 will be syearid =2001
here's my code:
<?php
require_once('auth.php');
include('connection/connect.php');
$idnum=$_POST['idnum'];
$stat='voted';
$sqla = "UPDATE student
SET status=?
WHERE idno=?";
$qa = $db->prepare($sqla);
$qa->execute(array($stat,$idnum));
$edittable=$_POST['votes'];
$a=1;
$N = count($edittable);
$YearNow=Date('Y');
for($i=0; $i < $N; $i++)
{
$sql = "UPDATE candidates,student,school_year
SET votes=votes+?
WHERE candid =? AND school_year.syearid = candidates.syearid
AND school_year.from_year like $YearNow ";
$q = $db->prepare($sql);
$q->execute(array($a,$edittable[$i]));
//I think because of this code
$sqlas = "INSERT INTO studentvotes(candid,idno,syearid) VALUES (:m,:n,:o)";
$qs = $db->prepare($sqlas);
$qs->execute(array(':m'=>$edittable[$i],':n'=>$idnum,':o'=>$YearNow));
}
header("location: notification.php?". http_build_query($query));
mysql_close($con);
?>
Need Help guys! thanks
I got your question finally:
after:
$qa->execute(array($stat,$idnum));
put something like: (I am not sure what database layer you use, this is mysqli example)
/** #var mysqli $con */
$years = array();
$data = $con->query('SELECT syearid, from_year FROM school_year')->fetch_all(MYSQLI_ASSOC);
foreach($data as $row)
$years[$row['syearid']] = $row['from_year'];
and update your
$qs->execute(array(':m'=>$edittable[$i],':n'=>$idnum,':o'=>$YearNow));
to
$qs->execute(array(':m'=>$edittable[$i],':n'=>$idnum,':o'=>$years[$YearNow]));
you can also query this after your operations:
UPDATE studentvotes AS sv
JOIN school_year AS sy ON sv.syearid = sy.syearid
SET sv.syearid = sy.from_year
and btw, please, put those preparations above the for loop, main purpose of prepare statements is efficiency
$q = $db->prepare("UPDATE candidates,student,school_year
SET votes=votes+?
WHERE candid =? AND school_year.syearid = candidates.syearid AND school_year.from_year like $YearNow");
$qs = $db->prepare("INSERT INTO studentvotes(candid,idno,syearid) VALUES (:m,:n,:o)");
for($i=0; $i < $N; $i++)
{
$q->execute(array($a, $edittable[$i]));
$qs->execute(array(':m'=>$edittable[$i],':n'=>$idnum,':o'=>$YearNow));
}

PDO SQL single query, multiple rows and values

I have this array JSON POST request to a PHP file.
Array
(
[user_id] => 1
[date] => 2014-12-05
[time] => 12:00
[description] => lol
[friends] => "12","9"
[PHPSESSID] => 5ae7c3e6339c528e7804020dd0f0cdbb
)
I try to add the values (12 | 1) and (9 | 1) to a mysql table with a single sql query
Table:
u_id | f_id
1 | 12
1 | 9
What I have so far:
$friendarray = $_POST['Friends'];
foreach( $friends as $friendsarray ) {
$values[] = "(" . $u_id . "," . $friendsarray . ")";
}
$query = "INSERT INTO up2_friends_to_users (u_id , f_id ) VALUES ".implode(',',$values);
$stmt = $db->prepare($query);
$result = $stmt->execute();
As you see this is not working at all. I try to achieve something like this:
$query_params = array(
':u_id' => $_POST['user_id'],
':f_id' => $friendid,
And then would like to send it like this:
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
Is it possible to create a single query with multiple rows like this?
Answer thanks to RobP:
$friendsarray = explode(',',$_POST['friends']);
$placeholders = [];
for($i=0, $len=count($friendsarray); $i < $len; $i++) {
$placeholders[i] .= "(:u_id".$i.", :f_id".$i.")"; // entries like "(:u_id0, :f_id0)"
}
$query = "INSERT INTO up2_friends_to_users (u_id , f_id ) VALUES ".implode(",", $placeholders);
$stmt = $db->prepare($query);
for($i=0, $len=count($placeholders); $i < $len; $i++) {
$stmt->bindParam(':u_id'.$i, $_POST['user_id']);
$nextFriend = $friendsarray[$i];
$stmt->bindParam(':f_id'.$i,trim($nextFriend,'"'));
}
$result = $stmt->execute();
Now f_id is always null.
I agree the best strategy is to use a single query as you were trying to do. This will be much faster for long lists, especially if you don't wrap all the individual inserts into a single commit. This should work:
$friendarray = $_POST['Friends'];
$placeholders = [];
$user_id = $_POST[`user_id`];
for($i=0, $len=count($friendarray); $i < $len; $i++) {
$placeholders[$i] = "(:u_id".$i.", :f_id".$i.")"; // entries like "(:u_id0, :f_id0)"
}
$query = "INSERT INTO up2_friends_to_users (u_id , f_id ) VALUES ".implode(",", $placeholders);
$stmt = $db->prepare($query);
for($i=0, $len=count($placeholders); $i < $len; $i++) {
// each binding must use a separate variable, not an array element
$stmt->bindParam(':u_id'.$i, $user_id);
// use your favorite escape function on the value here
$nextFriend = $db->real_escape_string($friendarray[$i]);
$stmt->bindValue(':f_id'.$i, $nextFriend);
}
EDIT: learned something new from Only variables can be passed by reference - php. Can't pass array elements to bindParam as second parameter! Workaround posted above.
Do this:
$query = "INSERT INTO up2_friends_to_users (u_id , f_id ) VALUES (:u_id, :f_id)";
$stmt = $db->prepare($query);
$stmt->bindParam(':u_id', $_POST['user_id'];
$stmt->bindParam(':f_id', $friendid);
foreach ($_POST['Friends'] as $friendid) {
$stmt->execute();
};
bindParam binds to a reference, so every time you execute the query it will use the value of $friendid from the current iteration of the loop.
Maybe, something like this (using question mark parameters)?
$values = array();
foreach ($_POST['Friends'] as $friendid) {
$values[] = $u_id;
$values[] = $friendid;
}
$conn = new \PDO($dsn, $user, $password);
$query = 'INSERT INTO up2_friends_to_users (u_id , f_id ) VALUES '
. trim(str_repeat('(?, ?),', count($values / 2)), ',');
$conn->prepare($query)->execute($values);

Categories