So I am trying to create multiple "result" arrays and fill them with the results of my statement, inside a "main" array.
However it seems to be that the result arrays are getting overwritten each time the loop passes instead of creating an additional array.
My code:
function retrieve_clients() {
$coach_id = $_SESSION['user_details']['id'];
$connection = connection();
$statement = select($connection, '*', 'relations', 'coach_id', '=');
$statement->bind_param('i', $coach_id);
if ($statement->execute()) {
$statement->bind_result($id, $coach_id, $client_id, $relation_date);
while ($statement->fetch()) {
$_SESSION['clients'] = array(
array($id, $coach_id, $client_id, $relation_date)
);
}
}
$statement->close();
}
I can't figure it out anymore. Please spare me because I'm still new to programming.
You need to add another dimension to your clients, by adding [] to the end of the variable.
$_SESSION['clients'][] = array(
array($id, $coach_id, $client_id, $relation_date)
);
I think the other answers are on the right track, however they add an unnecessary and probably undesirable dimension. Try adding a dynamic dimension with [] but remove one of the array dimensions that overwrites:
$_SESSION['clients'][] = array($id, $coach_id, $client_id, $relation_date);
That's because you're replacing the value of clients in following line,
$_SESSION['clients'] = array(
array($id, $coach_id, $client_id, $relation_date)
);
What you need to do is create an array of clients first before loop and then append each array in the main array.
Like this,
$_SESSION['clients']=array();
while ($statement->fetch()) {
$_SESSION['clients'][] = array(
array($id, $coach_id, $client_id, $relation_date)
);
Do yourself a favor, stop using mysqli (as well as that home-brewed "select" function).
With PDO you'll have a code that is sane:
function retrieve_clients() {
$sql = "SELECT * FROM relation WHERE coach_id = ?";
$statement = connection()->prepare($sql); // here goes your SQL
$statement->execute([$_SESSION['user_details']['id']]); // here goes a parameter
$_SESSION['clients'] = $statement->fetchAll(); // and here you have your array
}
Related
I'm having trouble with a PDO insert.
I'd like to insert a multidimensional array into a db, which i managed to.
However, i also would like to insert a completely different array, disconnected from the first one into the same db line (which means same INSERT query).
1st multidimensional array = $_POST['training']
2nd array = $training_diploma
Kinda difficult to explain. Here is the code :
$id = "1";
$training_diploma = ("master", "bachelor");
$id_training_key = $id_training_key = $id.'_'.$training_diploma; (return: 1_master)
$sql = "INSERT INTO users_resumes (id,resume_category,id_training_key,training_diploma,training_school,training_level,training_start_date,training_end_date) VALUES ($id,'Training',$id_training_key,:training_diploma,:training_school,:training_level,:training_start_date,:training_end_date)";
$stmt= $pdo->prepare($sql);
foreach($_POST['training'] as $params){
$stmt->execute($params);
}
Any help is very welcome !!! Thx a looot !
You need to iterate the $training_diploma array as well as the $_POST array. It would be better to also include the id and id_training_key as parameters in your query so you don't have to prepare a new query for each set of values:
$id = "1";
$training_diploma = array("master", "bachelor");
$sql = "INSERT INTO users_resumes (id,resume_category,id_training_key,training_diploma,training_school,training_level,training_start_date,training_end_date) VALUES (:id,'Training',:id_training_key,:training_diploma,:training_school,:training_level,:training_start_date,:training_end_date)";
$stmt= $pdo->prepare($sql);
foreach($_POST['training'] as $params) {
foreach ($training_diploma as $diploma) {
$stmt->execute(array_merge($params, array('id' => $id, 'id_training_key' => $id . '_' . $diploma)));
}
}
In my Android app I am using volley to echo my response which should be of the form:
[
{
"category":"whatever",
"name":"whatever",
"phone":"whatever",
"comment":"whatever",
"reviewid":32
},
{
"category":"whatever",
"name":"whatever",
"phone":"whatever",
"comment":"whatever",
"reviewid":76
}
]
Instead I am getting as the response:
Warning: Invalid argument supplied for foreach() in php file on line 13
I don't know if I'm posting it wrong from java or if the problem is with my php.
In my app I have:
selectOwnUserReviews = Arrays.toString(category.getUserReviewIds());
When I toast selectOwnUserReviews I can see selectOwnUserReviews is of the format like:
[63,59,42] or [234] or [34,29] etc...
I am trying to post the selectOwnUserReviews array with volley to my php with:
#Override
//post info to php
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
//KEY_REVIEWID_USER is reviewiduser
params.put(KEY_REVIEWID_USER, selectOwnUserReviews);
return params;
}
Here's my php:
<?php
require('file.php');
$ReviewID = $_POST['reviewiduser'];
$results = array();
foreach($ReviewIDs as $ReviewID) {
$sql2 = "SELECT * FROM review WHERE review_id = ?";
$stmt2 = $con->prepare($sql2) or die(mysqli_error($con));
$stmt2->bind_param('i', $ReviewID) or die ("MySQLi-stmt binding failed ".$stmt2->error);
$stmt2->execute() or die ("MySQLi-stmt execute failed ".$stmt2->error);
$result2 = $stmt2->get_result();
//if user_id has reviews in the db
while($row = mysqli_fetch_array($result2)) {
//make an array called $results
$results[] = array(
'category' => $row['cat_name'],
'name' => $row['name'],
'phone' => $row['phone'],
'comment' => $row['comment'],
'reviewid' => $row['review_id'],
);
}
}
$json = json_encode($results);
echo $json;
?>
Line 13 in my php code is:
foreach($ReviewIDs as $ReviewID) {
Different variable names!
$ReviewID = $_POST['reviewiduser']; //without final s
foreach($ReviewIDs as $ReviewID) { //with final s.
/* ^ */
Edit:
You say you're still having problems.
Probably your $_POST is a string. Try:
$reviewIDs = json_decode($_POST['reviewiduser']);
that will probably make $reviewIDs a proper array to work with foreach.
You're getting the foreach error when posting with Android Volley to your php b/c all input values are strings in PHP and you can not foreach a string.
You perhaps first want to decode the string into an array which you then can foreach over. But if that is inteded by you or not was not clear to me from your question so I can not be very specific about that w/ my answer and only provide this as a general hint. It is often common to decode JSON, but please that must not be the case in your case: http://php.net/json_decode.
Next to that also take care you are using actually the correct variables, as others have pointed out and I'm with them, you need to use the exactly correctly written variable name, even one character off will give you a different variable name which will evaluate to NULL in PHP which you can't foreach over as well like w/ the string.
I was posting from Java to Php as a string and treating it in php like it was an array.
In Java I was posting selectOwnUserReviews which was of the format like "[1,34,78]". It looked like an array but was all treated as one unit. And even if I tried operations in php I was doing it on [1 and 34 and 78], not on 1 and 34 and 78.
So in Java I did:
//convert [56,23,87] to a string
selectOwnUserReviews = Arrays.toString(category.getUserReviewIds());
//remove [ and ] so we have a string of 56,23,87
selectOwnUserReviews = selectOwnUserReviews.substring(1,selectOwnUserReviews.length()-1);
Now I have the string 56,23,87 and I post this string to my php. In my php I use explode to get the individual values, my complete code looking like:
<?php
require('file.php');
$ReviewID = $_POST['reviewiduser'];
$ReviewID = explode(",",$ReviewID);
$results = array();
foreach($ReviewID as $ReviewID) {
$sql2 = "SELECT * FROM review WHERE review_id = ?";
$stmt2 = $con->prepare($sql2) or die(mysqli_error($con));
$stmt2->bind_param('i', $ReviewID) or die ("MySQLi-stmt binding failed ".$stmt2->error);
$stmt2->execute() or die ("MySQLi-stmt execute failed ".$stmt2->error);
$result2 = $stmt2->get_result();
while($row = mysqli_fetch_array($result2)) {//make an array called $results
$results[] = array(
'category' => $row['cat_name'],
'name' => $row['name'],
'phone' => $row['phone'],
'comment' => $row['comment'],
'reviewid' => $row['review_id'],
);
}
}
echo json_encode($results);
?>
Job done.
To expand on the other answers and give a more complete response based on the comments.
As you noted var_dump($_POST) results in the following:
array(1) {
["reviewiduser"]=>
string(4) "[63]"
}
Meaning that your post value is being received as a string of 4 characters, in the format of an array. Rather than the desired array of integers. However you can use json_decode on the value to parse the string into the desired array of integers.
Example: https://3v4l.org/64v30
Since we should never trust the data from the end user, especially cross-platform/site, you should sanitize and validate the data prior to processing it.
Additionally you have a mix of procedural and object oriented uses of mysqli with some missing controls of where mysql can return false or null. It is best to only utilize one style to ensure that your code is cohesive/coherent. As well as cover all cases where a function/method call may return a value other than what is desired, to be better anticipate the desired behavior or failure.
<?php
require __DIR__ . '/file.php';
//ensure the desired post key exists otherwise set a default value
if (!array_key_exists('reviewiduser', $_POST)) {
$_POST['reviewiduser'] = '[]';
}
//ensure json decode returns as the desired data type
$ReviewIDs = json_decode($_POST['reviewiduser']);
if (!is_array($ReviewIDs)) {
$ReviewIDs = [];
}
//declare results for use with json_encode
$results = [];
//declare ReviewID as a variable for use with bind_param
$ReviewID = null;
//prepare the database query for multiple executions
$sql2 = 'SELECT cat_name AS category, name, phone, comment, review_id AS reviewid FROM review WHERE review_id = ?';
//create a statement for use with prepare
$stmt2 = $con->stmt_init();
$stmt2->prepare($sql2) or die($con->error);
//bind ReviewID by reference so that it changes for each execute iteration
$stmt2->bind_param('i', $ReviewID) or die ('MySQLi-stmt binding failed ' . $stmt2->error);
//iterate over ReviewIDs
foreach ($ReviewIDs as $ReviewID) {
//validate the value is of the desired type
if (!is_numeric($ReviewID)) {
continue;
}
$stmt2->execute() or die ('MySQLi-stmt execute failed ' . $stmt2->error);
$result2 = $stmt2->get_result() or die('MySQLi-smt get_result failed' . $smt2->error);
//ensure a result is retrieved from the database
if ($row = $result2->fetch_assoc()) {
$results[] = $row;
}
}
$stmt2->close();
echo json_encode($results);
exit;
This is a better explanation of the problem I woefully tried to explain previously .
I wish to execute multiple queries that all use the same prepared statement, like so (working code):
$params = [
['age'=>10,'id'=>1], ['age'=>12,'id'=>2],
];
$param_types = 'ii';
$sql_template = "UPDATE mytable SET age = ? WHERE id = ?";
$stmt = $mysqli->prepare($sql_template);
$stmt->bind_param($param_types, $age, $id);
foreach($params as $param):
$age = $param['age'];
$id = $param['id'];
$stmt->execute();
endforeach;
I'd like to put this logic in a function, and use it like so:
queries_from_template($sql_template, $params, $param_types);
I'm stuck trying to figure out how to write the function given that I don't know what $params will look like. Here is what I have so far:
function queries_from_template($sql_template,$params,$param_types){
//$mysqli is a handle to a live mysqli DB connection
$stmt = $mysqli->prepare($sql_template);
//build the array that holds the arguments of $stmt->bind_param
//result will be eg: ['ii', 10, 1]
$bind_param_args = array_merge([$param_types],array_values($params[0]));
//call bind_param with a dynamic number of arguments
call_user_func_array([$stmt,"bind_param"],$bind_param_args);
foreach($params as $param):
/* THIS IS WHERE I'M STUCK*/
// I need a handle to each of the parameters that were bound with
// bind_param so that I can set them to the correct value
// on each loop before I execute.
// Remember I don't know how many parameters there are
//run query with current value of parameters
$stmt->execute();
endforeach;
//todo: free results, close connection, disconnect
}
// Associated sets
$params_sets = [
['age'=>10,'id'=>1],
['age'=>12,'id'=>2],
];
// Param names in right order
$param_names = ['age', 'id'];
// Param types in right order
$param_types = 'ii';
// SQL template
$sql_template = "UPDATE mytable SET age = ? WHERE id = ?";
$stmt = $mysqli->prepare($sql_template);
// Ok, let's do it!
foreach ($params_sets as $params) {
// Collecting parameters for bind_param function
// You need to do it every iteration!
// First parameter is $param_types
$bind_params = [$param_types];
// Now let's add every parameter in right order using $param_names
foreach ($param_names as $param_name) {
$bind_params[] = $params[$param_name];
}
// Ok! Call bind_param method from $stmt object with $bind_params as parameters
call_user_func_array([$stmt, 'bind_param'], $bind_params);
// And execute query
$stmt->execute();
}
Good luck!
Since the specific var names used to refer to parameters in $stmt->bind_param(...) don't matter, I changed the input from an associative array to an indexed array
Before:
$params = [
['age'=>10,'id'=>1], ['age'=>12,'id'=>2],
];
Now:
$params = [
[10,1], [12,2],
];
This makes it easier to loop through parameters on each query. Below is my solution:
/*
* Executes multiple queries from the same prepared statement
*/
function queries_from_template($sql_template, $params, $param_types){
$stmt = $mysqli->prepare($sql_template);
$handles = [];//holds references to parameters
for($i=0; $i<count($params[0]);$i++):
$varname = "param_$i";
$$varname = null; //define variables $param_0, $param_1...
$handles[] = &$$varname; //store references to the new variables
endfor;
//call $stmt->bind_param: bind to the new variables
$bind_param_args = array_merge([$param_types],$handles);
call_user_func_array([$stmt,'bind_param'],$bind_param_args);
foreach($params as $param):
foreach($handles as $index => &$handle):
// assign the values for the current execute loop
// to the created vars ($param_0, $param_1...)
$handle = $param[$index];
endforeach;
$stmt->execute(); //execute, todo: error handling
endforeach;
$stmt->close(); $mysqli->close();
}
I'm using MySQLi for a project (but could easily switch to PDO, read the edit at the bottom). What I want is to be able to process an associative array into an mysql query without typing every key/value pair manually. I know that might be simply put, but I'm still in a learning process when it comes to MySQLi/PDO. To make it clear what I mean here is an example:
Let's say I have this associative array:
$data = array(
'name' => 'Objective',
'short_name' => 'OB',
'generation' => 1
);
What I want is to create a query like this:
UPDATE signals SET ? = ? WHERE sid = '1'
And it to become a query like this (or instead of an UPDATE, an INSERT, SELECT etc.):
UPDATE signals SET
name = 'Objective',
short_name = 'OB',
generation = 1
WHERE sid = '1'
So basically my question is: is this possible with MySQLi or PDO itself? If it's possible how would I have to do this?
I've read on preparing statements before executing, but it's not getting to me yet. Hopefully someone can help me out.
Edit: I'm still very early into the project so i'm also fine with using PDO, but the same question remains. I did look into both PDO and mysqli, and I'm not sure how to do it with PDO either. So for the sake of the question i'll add PDO to as a tag and to the question.
Here is a function that will take an input array and produce something you can drop straight into your query, as long as you are only updating a single table:
function array_to_pdo_params($array) {
$temp = array();
foreach (array_keys($array) as $name) {
$temp[] = "`$name` = ?";
}
return implode(', ', $temp);
}
Now you can perform your PDO query like this:
// The data
$data = array(
'name' => 'Objective',
'short_name' => 'OB',
'generation' => 1
);
// The parameter for the WHERE clause
$sid = 1;
// Build the query string
$query = "
UPDATE `signals`
SET ".array_to_pdo_params($data)."
WHERE `sid` = ?
";
// Convert the data array to indexed and append the WHERE parameter(s) to it
$queryData = array_values($data);
$queryData[] = $sid;
$stmt = $db->prepare($query); // Obviously add the appropriate error handling
$stmt->execute($queryData);
You can use the PDO's execute statement by passing an array of input values, for a prepared query. For reference: http://php.net/manual/en/pdostatement.execute.php
Example:
<?php
/* Execute a prepared statement by passing an array of insert values */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour');
$sth->execute(array(':calories' => $calories, ':colour' => $colour));
?>
Zend_DB will let you do exactly that.
Tho we created a wrapper over it my work to make it simpler, I really think it's as simple as you can get, and it does all the magical stuff for you.
You really should give it a try; big time saver once you learn it.
How about building the query with http_build_query as follows:
$columns = array(
'name',
'short_name',
'generation', ...
);
$data = array(
'name' => 'Objective',
'short_name' => 'OB',
'generation' => 1
);
$to_update = array_intersect_key($data, array_flip($columns));
$update_string = urldecode(http_build_query(array_combine(array_keys($columns), array_fill(0, count($columns), '?')), '', ', '));
try {
$stmt = $db->prepare(sprintf('UPDATE table SET %s WHERE condition=?', $update_string));
$result = $stmt->execute(array_values($data));
...
} catch (\PDOException $ex) {
}
Be sure to have the parameter for the WHERE condition at the end of your $data array.
Please see below my code.
I am attempting to bind an array of paramenters to my prepared statement.
I've been looking around on the web and can see I have to use call_user_func_array but cannot get it to work. The error I get is:
"First argument is expected to be a valid callback, 'Array' was given"
I may be wrong but I'm assuming the first argument can be an an array and perhaps this error message is misleading. I think the issue is that my array is in someway at fault.
Can anyone see what I am doing wrong? Thanks.
$type = array("s", "s");
$param = array("string1","anotherstring");
$stmt = $SQLConnection->prepare("INSERT INTO mytable (comp, addl) VALUES (?,?)");
$params = array_merge($type, $param);
call_user_func_array(array(&$stmt, 'bind_param'), $params);
$SQLConnection->execute();
It must be like this:
//connect
$mysqli = new mysqli($host, $user, $password, $db_name);
//prepare
$stmt = $mysqli->prepare("SELECT * FROM the_table WHERE field1= ? AND Field2= ?");
//Binding parameters. Types: s = string, i = integer, d = double, b = blob
$params= array("ss","string_1","string_2");
//now we need to add references
$tmp = array();
foreach($params as $key => $value) $tmp[$key] = &$params[$key];
// now us the new array
call_user_func_array(array($stmt, 'bind_param'), $tmp);
$stmt->execute();
/* Fetch result to array */
$res = $stmt->get_result();
while($row = $res->fetch_array(MYSQLI_ASSOC)) {
$a_data[]=$row;
}
print_r($a_data);
$stmt->close();
Since PHP 5.6, you don't have to mess around with call_user_func_array() anymore.
Instead of:
$stmt->bind_param($param_types, $my_params_array);
you can just use the splat operator, like this:
$stmt->bind_param($param_types, ...$my_params_array); // exact code
I wouldn't know why you have to use call_user_func_array, but that's another story.
The only thing that could be wrong in my eyes is that you are using a reference to the object. Assuming you're using PHP 5.*, that is not necessary:
call_user_func_array(array($stmt, 'bind_param'), $params);
If you get an error, you should try this:
call_user_func_array(array($stmt, 'bind_param'), refValues($params));
function refValues($arr){
if (strnatcmp(phpversion(),'5.3') >= 0) {
$refs = array();
foreach($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
return $arr;
}
Wasn't able to answer this on my own question because it got marked as dupe: here. But I think my final solution, which uses the answers in this question, works in my use case, might be helpful for someone.
My goals was to take a posted set of ID's and use them in a NOT IN MYSQL statement. Assuming array of 5 ID's posted.
Count the number posted ID's to build the ? placeholders for NOT IN statement. Using $params_count = substr(str_repeat(',?', count($array_of_ids)), 1); gives the result: (?,?,?,?,?) to be used in SQL statement.
Make function that takes ID's and type i or s etc. For me, they were all i so my function is simpler. return array that looks like this $params= array("iiiii",1,2,3,4,5) where the first value is the set of i's and the subsequent values are the ID's depending on total ID's passed into function.
function build_bind_params($values, $bind_type) {
$s = substr(str_repeat($bind_type, count($values)), 0);
$bind_array = array();
$bind_array[] = $s;
foreach($values as $value) {
$bind_array[] = $value;
}
return $bind_array;
}
$params = build_bind_params($array_of_ids, "i");
Then use foreach ($params as $key => $value) $tmp[$key] = &$params[$key]; to get the newly created $params formatted properly for binding.
Then use call_user_func_array(array($stmt , 'bind_param') , $tmp); to properly bind the array.
Then execute the $stmt
Most of the above are not solutions without integrating the types along with the values before adding them to call_user_func_array(). This solution worked for me:
/* create a database connection */
$db = new mysqli($host, $user, $password, $db_name);
/* setup the sql, values, and types */
$sql="SELECT * FROM languages
WHERE language_code = ?
AND charset = ?
ORDER BY native_name";
$values = array($langCode, $charset);
$types = "ss";
/* pass those variables to the execute() function defined below */
if ($rows = execute($sql, $values, $types))
{
return $rows[0];
}
function execute($sql, $values='', $types='')
{
/* prepare the sql before binding values and types */
$stmt = $db->prepare($sql);
/*combine the values and types into $inputArray */
$inputArray[] = &$types;
$j = count($values);
for($i=0;$i<$j;$i++){
$inputArray[] = &$values[$i];
}
/* add the combined values and types to call_user_func_array() for binding */
call_user_func_array(array($stmt, 'bind_param'), $inputArray);
$result = $stmt->execute();
return $result;
}
Here's a reference to the full description this example is based on:
http://big.info/2015/08/php-use-call_user_func_array-for-variable-number-of-parameters-arrays-in-prepared-statements.html
Why would you want to call call_user_func_array(array($statement, 'bind_param'), $bind_arguments)? Because $bind_arguments is an array. You get to have one function that binds a statement to its queried parameters, no matter how many parameters you'd have otherwise.
Example of good code...
<?php
# link
$dblink = new mysqli('HOSTNAME','USERNAME','PASSWORD','DATABASENAME');
# example data
$statement = $dblink->prepare("SELECT * from Person WHERE FirstName = ? AND MiddleName = ? AND LastName = ? and Age = ?");
$recordvalues = ['John', 'H.', 'Smith', 25];
$sqlbindstring = "sssi"; # String, String, String, Integer example
# make the references
$bind_arguments = [];
$bind_arguments[] = $sqlbindstring;
foreach ($recordvalues as $recordkey => $recordvalue)
{
$bind_arguments[] = & $recordvalues[$recordkey]; # bind to array ref, not to the temporary $recordvalue
}
# query the db
call_user_func_array(array($statement, 'bind_param'), $bind_arguments); # bind arguments
$statement->execute(); # run statement
$result = $statement->get_result(); # get results
# get the results
if($result) {
while ($row = $result->fetch_assoc()) {
print("\n\nMy row is...");
print_r($row);
}
}
?>
Example of bad code...
<?php
# Same setup as above..
$statement->prepare("SELECT * from Person WHERE FirstName = ? AND MiddleName = ? AND LastName = ? and Age = ?");
$statement->bind('John', 'H.", 'Smith', 25);
?>
In the first example: You can pass as much or as little to the binding to be done, so that bind() might be called in only one line in your entire application. This scales well.
In the second example: You must write one bind() statement for every possible group of insertions for every possible record in your database. This scales poorly.