How to insert array into mysql using PDO and bindParam? - mysql

I'm using the following code. The code works, but I want to change it so that it uses bindparam
try {
$dbh = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password);
$stqid=array();
for ($i=0; $i<$array_count; $i++){
$stqid[$i][0]=$lastInsertValue;
$stqid[$i][1]=$qid[$i][0];
$stqid[$i][2]=$qid[$i][1];
}
$values = array();
foreach ($stqid as $rowValues) {
foreach ($rowValues as $key => $rowValue) {
$rowValues[$key] = $rowValues[$key];
}
$values[] = "(" . implode(', ', $rowValues) . ")";
}
$count = $dbh->exec("INSERT INTO qresults(instance, qid, result) VALUES ".implode (', ', $values));
$dbh = null;
}
catch(PDOException $e){
echo $e->getMessage();
}
I replaced the following
$count = $dbh->exec("INSERT INTO qresults(instance, qid, result) VALUES ".implode (', ', $values));
with
$sql = "INSERT INTO qresults (instance, qid, result) VALUES (:an_array)";
$stmt = $dbh->prepare($sql);
$stmt->bindParam(':an_array', implode(',', $values),PDO::PARAM_STR);
$stmt->execute();
but the insert doesn't work anymore (I didn't get any error messages though).
QUESTION: What am I doing wrong? How can I rewrite the code to use bindParam?

You're trying to create a statement and bind a param.
Statement are great because it potentially nullify any kind of SQL injection. And it does it by removing the concept of a query being only seen as a string. The SQL query is seen as a string with a parameter list and an the associated data as binded variables.
So the query is not only text, but text + data.
I mean:
This simple query:
SELECT * FROM A WHERE val="$param"
It is not safe because the query is only viewed as a string. And if $param is not checked, it is a SQLi hole.
But when create a statement, your query becomes:
SELECT * FROM A WHERE val=:param
Then you use bindparam to specify the value a :param. Which mean the value is not appended to the query string, but the query is already parsed and the data is provided.
In your case, you bind to the param :array an imploded array (I assume "data1", "data2", etc..). Which is only one parameter with the value as a string ( "data1, data2, data3..." ), so it will only result in one insert and not multiple insertions.
You can change your statement generation by generating a query with enough parameters to handle your array
$sql = "INSERT INTO qresults (instance, qid, result) VALUES ( :val0, :val1, :val2, ...)";
Then loop on your array and call the bindparam method for each parameters.
$count = 0;
foreach($values as $val)
{
$stmt->bindParam(":val$count", $val,PDO::PARAM_STR);
$count++;
}
This will work.
Edit: This solution show how it works for a one dimensional array, but can be easily extended to your problem by tweaking the statement query generation and modify the bindparam loop.
Your statement should looks like:
$sql = "INSERT INTO qresults (instance, qid, result) VALUES (:val0, :val1, :val2) , (:val3, :val4, :val5), ...";
You just have to count the number of element in your base array.

Related

PDO and rowcount

I'm trying to make the PDO query below working, but somehow it doesn't.
This is how far i mine brain exploded!
Is there somebody who can say what i'm doint wrong?
$dbh = new PDO("mysql:host=localhost;dbname=databasename", "user", "pass");
$stmt = $dbh->prepare("SELECT * FROM `something` WHERE `email`=':email' and `name`=':name'");
$stmt->bindParam(':email', 'mail#mail.nl',PDO::PARAM_STR);
$stmt->bindParam(':name', 'name',PDO::PARAM_INT);
$stmt->execute();
if($stmt->rowCount() == 1)
{
echo "row count 1";
}
else if ($stmt->rowCount() == 0)
{
echo "row count 0";
}
else if ($stmt->rowCount() > 1)
{
echo "row count greater then 1";
}
Single quotes (') denote string literals in SQL, so the prevent PDO from parsing inside them. If you want to use bind variables, you should remove the quotes (don't worry, PDO will properly handle them as strings):
$stmt = $dbh->prepare
("SELECT * FROM `something` WHERE `email`=:email and `name`=:name");

Mysql dynamic sql statement with PDO and parameters

I want to create a dynamic sql statement and run it using PDO. My problem is that i have some parameters and i cannot think of a way to pass the parameters.
Ex :
$query = "Select * from tbl_task where 1=1";
if (!empty($name)) $query .= " AND name = ?";
if (!empty($status)) $query .= " AND status = ?"
$db_stmt = new PDOStatement();
$db_stmt = $this->db->prepare($query);
$db_stmt->bindParam (1,$name);
$db_stmt->bindParam (2,$status);
My parameters does not get binded and i don't know how many parameters i have to bind, unless i write the same if statements but with bindParam instructions.
I tryed with mysql_real_escape_string instead bindParam to PDO but for some reason my parameters are added empty.
Any idea on how can i build a dynamic query and bind parameters to PDO ?
Edit 1 :
$arr = array();
if (!empty($name)){
$query .= " AND `name` like :NAME";
$arr['NAME'] = $name;
}
$db_stmt = new PDOStatement();
$db_stmt = $this->db->prepare($query);
$db_stmt->execute($arr);
How can i write a "like" statement ? I tried
$query .= " AND `name` like :NAME" . "%";
and is not working.
What I usually do is the following:
$query = "Select * from `tbl_task` where 1=1";
$arr = array();
if (!empty($name))
{
$query .= " AND `name` = :NAME";
$arr['NAME'] = $name;
}
if (!empty($status))
{
$query .= " AND `status` = :STATUS";
$arr['STATUS'] = $status;
}
$this->db->beginTransaction();
try
{
$tmp = $this->db->prepare($query);
$tmp->execute($arr);
$this->db->commit();
}
catch(PDOException $ex)
{
$this->db->rollBack();
$this->log->error($ex->getMessage());
}
You can't add SQL code as a parameter; only data will do. You'll have to force these bits into $query. They won't be escaped then so they shouldn't contain user-submitted data.
What I usually do is the following:
$query = "Select * from tbl_task where 1=1";
if (!empty($name)) $query .= $db->parse(" AND name = ?s", $name);
if (!empty($status)) $query .= $db->parse(" AND status = ?s",$status);
$data = $this->db->getAll($query);
the idea is in having a function to parse placeholders in arbitrary query part instead of whole query.
I don't bother with native prepared statements though. They pollute PHP scripts with heaps of useless code with not a single benefit.
To answer updated question
as you've been told, you can't bind arbitrary query part. But a literal only.
So, make your literal looks like foo% and then bind it usual way.

PHP PDO succinct mySQL SELECT object

Using PDO I have built a succinct object for retrieving rows from a database as a PHP object with the first column value being the name and the second column value being the desired value.
$sql = "SELECT * FROM `site`"; $site = array();
foreach($sodb->query($sql) as $sitefield){
$site[$sitefield['name']] = $sitefield['value'];
}
I now want to apply it to a function with 2 parameters, the first containing the table and the second containing any where clauses to then produce the same result.
function select($table,$condition){
$sql = "SELECT * FROM `$table`";
if($condition){
$sql .= " WHERE $condition";
}
foreach($sodb->query($sql) as $field){
return $table[$field['name']] = $field['value'];
}
}
The idea that this could be called something like this:
<?php select("options","class = 'apples'");?>
and then be used on page in the same format as the first method.
<?php echo $option['green'];?>
Giving me the value of the column named value that is in the same row as the value called 'green' in the column named field.
The problem of course is that the function will not return the foreach data like that. That is that this bit:
foreach($sodb->query($sql) as $field){
return $table[$field['name']] = $field['value'];
}
cannot return data like that.
Is there a way to make it?
Well, this:
$sql = "SELECT * FROM `site`"; $site = array();
foreach($sodb->query($sql) as $sitefield){
$site[$sitefield['name']] = $sitefield['value'];
}
Can easily become this:
$sql = "SELECT * FROM `site`";
$site = array();
foreach( $sodb->query($sql) as $row )
{
$site[] = $row;
}
print_r($site);
// or, where 0 is the index you want, etc.
echo $site[0]['name'];
So, you should be able to get a map of all of your columns into the multidimensional array $site.
Also, don't forget to sanitize your inputs before you dump them right into that query. One of the benefits of PDO is using placeholders to protect yourself from malicious users.

Why am I getting error SQLSTATE[HY093]: Invalid parameter number: ? How can I fix it?

Based on this question How to insert array into mysql using PDO and bindParam?
I'm trying to insert values of an array into mysql via PDO.
I'm having a hard time of it, because I keep getting the following error.
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
for this line $stmt->execute();
I'm guessing the problem has something to do with this line
$stmt->bindParam(':val$count', $val,PDO::PARAM_STR); Specifically 'val$count', but I'm not sure exactly what is going wrong.
QUESTION: What am I doing wrong? How can I fix this?
Anyway here is the code I'm using along with the sample array.
$lastInsertValue=87;
$qid[0][0]=1;
$qid[0][1]=1;
$qid[1][0]=2;
$qid[1][1]="null";
$qid[2][0]=3;
$qid[2][1]=0;
$array_count = count($qid);
if (isset($lastInsertValue))
{
try
{
$dbh = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password);
$stqid=array();
$a=0;
for ($i=0; $i<$array_count; $i++)
{
$stqid[$a]=$lastInsertValue;
$a++;
$stqid[$a]=$qid[$i][0];
$a++;
$stqid[$a]=$qid[$i][1];
$a++;
}
$sql = "INSERT INTO qresults (instance, qid, result) VALUES ( :val0, :val1, :val2)";
$count = 0;
$stmt = $dbh->prepare($sql);
foreach ($stqid as $val)
{
$stmt->bindParam(':val$count', $val,PDO::PARAM_STR);
$count++;
}
$stmt->execute();
$dbh = null;
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
Yikes, so many issues.
Your array building is so verbose. Try this
$stqid = array();
foreach ($qid as $qidArr) {
$stqid[] = $lastInsertValue; // no idea why you repeat this
$stqid[] = $qidArr[0];
$stqid[] = $qidArr[1];
}
Use positional placeholders if you're simply relying on number of arguments
$sql = 'INSERT INTO ... VALUES (?, ?, ?)';
bindParam uses references which you are overwriting with each loop iteration. You would want to use bindValue() instead
Your query only has 3 placeholders but your $stqid array has 9 items. This is the source of your error.
You have single quotes around a variable, which will be treated as the variable name $count (not the value), try concatenating the variable to the string. Give this a try:
$stmt->bindParam(':val' . $count, $val,PDO::PARAM_STR);
for ($i=0; $i<$array_count; $i++)
{
$stqid[$a]=$lastInsertValue;
$a++;
$stqid[$a]=$qid[$i][0];
$a++;
$stqid[$a]=$qid[$i][1];
$a++;
}
so in case $i = 2, it will add $stqid[6], $stqid[7], $stqid[8] so
foreach ($stqid as $val)
{
$stmt->bindParam(':val$count', $val,PDO::PARAM_STR);
$count++;
}
will give you :val0 to :val8
In your query you have only :val0 to :val2.
Also having multiple values in one field in database is bad. Don't do it. Try to redesign your DB differently
EDIT: bad math in the morning... sorry

JSON encode comma delimited row

I'm trying to add an autocomplete tokenizer script to some form fields and one issue I'm having is if a person saves multiple values for the field the autocomplete suggestions come back with all of his values as one long value instead of them being single values delimited by the comma. I first tried to simply explode the value but it doesn't format it correctly in the JSON encode.
Here is my PHP file:
//connection information
$host = "localhost";
$user = "myuser";
$password = "mypass";
$database = "mydb";
$param = ($_GET["term"]);
//make connection
$server = mysql_connect($host, $user, $password);
$connection = mysql_select_db($database, $server);
//query the database
$query = mysql_query("SELECT cb_activities FROM jos_comprofiler WHERE cb_activities REGEXP '^$param'");
//build array of results
for ($x = 0, $numrows = mysql_num_rows($query); $x < $numrows; $x++) {
$row = mysql_fetch_assoc($query);
$activities[$x] = array(cb_activitiesterm => $row[cb_activities]);
}
//echo JSON to page
$response = $_GET["callback"] . "(" . json_encode($activities) . ")";
echo $response;
mysql_close($server);
This gives the output like this:
[{"cb_activities":"Kicking Cats,"},{"cb_activities":"baseball,hockey,"}]
but I need it to output like this:
[{"cb_activities":"Kicking Cats,"},{"cb_activities":"baseball,"},"cb_activities":"hockey,"}]
I also need to find a way to prevent duplicate entries from populating. For instance, the way it is now, say 10 people all have kicking cats selected as a value, it will display 10 times in the autocomplete suggestions.
How do I set this up to correctly delimit at the commas and then weed out duplicate values?
NM the duplicate issue, I just added select distinct instead of just select, this json thing has me overcomplicating things now lol. Now if I can just figure out how to delimit properly at the comma all will be good.