I am creating an array from dynamic created input fields and then send the array over to php, in php I create a foreach loop and try to insert it into my database.
I have created a foreach loop for my array and insert the values like that in my database, however my problem is: You cannot bind a variable with an index number to your query string. I have already verified whether it's actually an array what I am sending and the answer is: Yes. it is an array.
$stmt = $conn->prepare('INSERT INTO vv(event_id, vvType, vvCosting) VALUES (?, ?, ?)');
foreach ($example as $index => $value) {
$stmt->bind_param('iss', $id, $example, $example_costs);
$id = $id;
$example = $value;
$example_costs = example_costs[$index]; //this DOES NOT work
$stmt->execute();
}
So what I want would be the following: Create one foreach loop for my $example variable and based on the index numbers it has also insert the data from the variable example_costs and insert that in the database as well. You shouldn't worry about whether the index value exists or not in my example_costs variable, since they're "pairs".
I know the problem is that I cannot bind my variable like this:
$example_costs[$index]
So now my question is: How can I bind my variable like the above? But in a way php DOES accept? Or do I need to create 2 foreach loops? --> Something I would rather NOT do.
You didn't specify what exactly you mean by "doesn't work" (i.e. you didn't describe what is going wrong or what behaviour you're seeing), but here's what I can observe just from the code:
1) You have (what appears to be) a typo: I expect example_costs[$index]; should really be $example_costs[$index];.
2) Assuming 1 is fixed, you're also overwriting the values of the variables $example and $example_costs, which are the ones you're trying to loop over, whilst the loop is still going on. Clearly this will destroy the original arrays and make them impossible to re-use next time it tries to loop.
In this situation, and as a general point of good coding practice, don't re-use variable names to represent two different things in the code - especially when you are still in the middle of using them for their original purpose!
In reality you don't really need these separate variables in the bind statement anyway. This should do the job:
$stmt = $conn->prepare('INSERT INTO vv(event_id, vvType, vvCosting) VALUES (?, ?, ?)');
foreach ($example as $index => $value) {
$stmt->bind_param('iss', $id, $value, $example_costs[$index]);
$stmt->execute();
}
The rest is either incorrect (over-writing the $example array, for instance) or redundant ($id = $id for instance - assigning a variable its own value is the very definition of pointless).
Related
This question already has answers here:
Use one bind_param() with variable number of input vars
(5 answers)
Dynamically bind params in $bind_param(); Mysqli
(2 answers)
Closed 5 years ago.
I am trying to construct a prepared statement dynamically and I keep getting the following error:
mysqli_stmt_bind_param(): Number of elements in type definition string
doesn't match number of bind variables in
When I echo my statements the number of type definitions matches the bind variable so I don't know what is wrong. I think my code may be passing in strings, quotes or something instead of variables but I'm new to prepared statement and not sure how to check my query. When using simple mysqli_query I can echo the query and see were my error is at. I'm not sure how to do this with prepared statements so I'm hoping someone can help me with uncovering my error.
I am trying to construct the prepares statement dynamically so that I can reuse the code as follows:
$db = mysqli_stmt_init($dbconnection);
//i have looped through my fields and constructed a string that when echoed returns this: ?, ?, ?, ?, I use sub str just to remove the last comma and space leaving me with the string ?, ?, ?, ?. Ive echoed this to the browser to make sure it is correct.
$preparedQs = substr($preparedQs, 0, -2);
//I then loop through each field using their datatype and constructs the type string as follows ssss. Ive echoed this to the browser to make sure it is correct.
$preparedType = 'ssss';
//I then loop through my post array verifying and cleaning the data and then it constructing a string of clean values that results in Mike, null, Smith, Sr., (First, Middle, Last, Suffix) I use substr again just to remove the last comma and space. Ive echoed this to the browser to make sure it is correct.
$cleanstr = substr($cleanstr, 0, -2);
//I then explode that string into a an array that I can loop through and assign/bind each value to a variable as follows and use substr again to remove last comma and space.
$cleanstr = explode(", ", $cleanstr);
$ct2 = 0;
foreach ( $cleanstr as $cl){
$name = "a".$ct2;
$$name = $cl;
$varstr .= "$".$name.", ";
$ct2 = $ct2 +1;
}
$varstr = substr($varstr, 0, -2);
//ive echoed the $varstr to the browser and get $a1, $a2, $a3, $a4. I have also echo their value outside of the loop and know values have been assigned.
//I then try to assign each step above the appropriate prepared statement place holder
$stmt = mysqli_stmt_prepare($db, "INSERT INTO Contacts VALUES (". $preparedQs. ")");
mysqli_stmt_bind_param($db, "'".$preparedType."'", $varstr);
mysqli_stmt_execute($stmt);
I'm am not sure what I am doing wrong because when I echo $preparedQs, $preparedType and $varstr they all have the same number of elements yet I'm getting the "mysqli_stmt_bind_param(): Number of elements in type definition string doesn't match number of bind variables in.." error. All i can think is that I have quotes or something where I shouldn't but I've tried adding and removing quotes in certain areas and cant get the error to resolve.
Also, I read some posts about passing null in prepared statement but even when I replace the null with an actual value, I still get the same error.
It's probably worth noting that when using simple procedural mysqli_query and mysqli_real_escape_string to clean my data things work fine. I am trying to improve my security by converting my application to prepared statement simply for the added security.
// EDIT / DIFFERENT
This question is different for two reason
I am using procedural coding and not object or PDO. So being new to prepared statements, the examples given aren't helpful even after trying to make sense of them.
I am using an insert statement, not a select or update statement which in procedural php the query string is writen differently for insert than for select or update statements.
Will someone help me with the procedural insert solution to my problem. Thank you.
As titled, I'm using MySQL PDO with a prepared statement, and when executing, I'm getting a single value (the last supplied value) pushed into all fields.
Table looks like so:
id (int - auto-increment)
a_id (int)
b_id (int)
INSERT looks like this:
INSERT INTO my_table(a_id, b_id)
VALUES (:a_id, :b_id)
Code to insert looks like this...
$stmt = $conn->prepare($sql);
foreach($params as $k => $v) {
$stmt->bindParam(
$k,
$v
);
}
$stmt->execute();
The statement successfully inserts a value (which confirms $conn is present, active and open and the statement is set up and parameters bound).
I have confirmed the values by debugging inside the foreach, where I get two parameters (in $params) of ":a_id" => 1, ":b_id" => 9999...
My record goes in as ({new_id}, 9999, 9999) though.*
This means it sees and uses ":b_id" => 9999, but appears to drop or overwrite ":a_id" => 1...
I thought maybe the underscore was a problem (but apparently several people have reviewed source in the past to indicate underscore is fine... though hyphen is not).
I'm not reusing parameters (my understanding is one use allowed per prepared statement)...
Is there some named parameter issue I'm missing here? Since it's basically ->bindParam() to ->execute(), there's not a lot of room for me troubleshoot.
I'm trying to address speed issues with ORM-based data access while using the Fat Free Framework, but I can't think that there's interference there.
For reference, this is running under PHP 5.5.8 / Windows / IIS.
EDIT:*
I can confirm that moving to positional parameters is doing the same thing.
INSERT INTO my_table(a_id, b_id)
VALUES (?, ?)
Code changed to...
$stmt = $conn->prepare($sql);
$i = 1;
foreach($params as $value) {
$stmt->bindParam(
$i,
$value
);
$i++;
}
$stmt->execute();
To clarify how the $params array is being set... things are being passed through to this code (which is the heart of an abstract db handler), and the array is manually constructed...
i.e.
$results = \XX\DB::do_cmd(
$db,
self::SQL_INSERT,
array(
':a_id' => intval($this->a_id),
':b_id' => intval($this->b_id),
)
);
Still got ({new_id}, 9999, 9999)...
NOTE: To remove confusion, in addition to going to a positional based pass, I also hardcoded values to see what I'd get...
$results = \XX\DB::do_cmd(
$db,
self::SQL_INSERT,
array(
1,
1234,
)
);
My record came out ({new_id}, 1234, 1234). Same problem, differing values.
I have the feeling there's a "gotcha" here... but I have no idea what it is.
The interesting thing, is that I double check the table for an existing record before the INSERT based on those two values (to prevent duplication) and the check correctly identifies that the record is there... despite it being wrong in the database... which means SELECT and INSERT are doing the same thing with the parameters (though not all that surprising, since parameter handling is the same).
EDIT2:
Update to note as solved. Using...
$stmt->execute($params); // scrap the foreach nonsense...
bindValue() rather than bindParam() is also appropriate.
NOTE: I was working from the following PHP documentation (http://php.net/manual/en/pdo.prepared-statements.php) which doesn't differentiate bindValue() vs bindParam()...
Thanks for all the help everybody!
I went with Michael's solution, but tested Ryan's too.
i.e.
Update to note as solved. Using...
$stmt->execute($params); // scrap the foreach nonsense...
bindValue() rather than bindParam() is also appropriate.
To wrap things up, as per Ryan's comment, I'm pushing an answer out.
Thanks again!
The variable has to be call-by-reference:
$stmt = $conn->prepare($sql);
foreach($params as $k => &$v) {
$stmt->bindParam(
$k,
$v
);
}
$stmt->execute();
See the first user-contributed comment here:
http://php.net/manual/en/pdostatement.bindparam.php
I had an issue where I executed a stored procedure with variables:
INSERT INTO table_walkinleads
VALUES('', FirstName, LastName, Phone, Email, NOW())
but although this was working perfectly in phpmyadmin. It was not the case in my php code.
And the MAIN reason was because of this.
foreach($params as $name => $value ) {
$this->query->bindParam($name,$value);
}
Look carefully at the $value variable in the foreach statement. Before was like that. But after some investigation, I come to realize it needed to be &$value. But WHY? What is the difference? Why just adding that reference in the value variable makes the difference in making my stored procedure work correctly?
foreach($params as $name => &$value ) {
$this->query->bindParam($name,$value);
}
PS: the error was still executing the stored procedure, BUT the last variable was inserted as the same value for ALL the variables in the stored procedure. Making it look like I just entered the SAME variable for all the stored procedure variables.
Does anyone have an explanation for this behavior and the reason behind it?
I'm trying to add data to a database using SQLite3 in PHP. I got it working without prepared statements but now I'm trying to make it safer. I'm not using PDO.
So far the following code doesn't work. It just inserts the words ":name" and ":email" into the database, instead of what their bound values should be:
$smt = $db->prepare("insert into names (name, email) values (':name', ':email')");
$smt->bindValue(':name', $var_name);
$smt->bindValue(':email', $var_email);
$var_name = ($_POST[post_name]);
$var_email = ($_POST[post_email]);
$smt->execute();
So I thought at first that this was because I have single quotes around :name and :email in the prepared statement. So I took those out. Now when I post the form, it just puts blank entries into the database, it doesn't insert the values of $var_name and $var_email
The statement is executing, it's just not binding the variables properly I don't think. What have I done wrong?
You managed to confuse binding functions.
It is bindParam have to be used if you don't have your variable assigned yet.
While bindValue have to be used with existing value only.
Also, you should turn error reporting ON
You don't need intermediate variables, you must do this:
$smt = $db->prepare("insert into names (name, email) values (':name', ':email')");
$smt->bindValue(':name', $_POST['post_name'], SQLITE3_TEXT);
$smt->bindValue(':email', $_POST['post_email'], SQLITE3_TEXT);
$smt->execute();
As documented in SQLite3Stmt::bindValue() value is binded instantly, not as SQLite3Stmt::bindParam() that gets the value of the variable at execute() time. So the problem is that that variables are empty when the statement is executed.
Remember:
You don't need to add parentheses on variable assignment: $a = ($b); -> $a = $b;
You MUST quote variable key name. Otherwise PHP will try to look for a constant with this name and will throw a warning if it doesn't exists... but will assign a erroneous key value if it exists!! $_POST[post_name] -> $_POST['post_name']
The following topic is close to what I'm trying to ask, but it's outdated considering the mysql functions are deprecated in php now and there are prepared statements for preventing sql injection. insert all $_POST data into mysql using PHP?
Basically, I have a huge number of columns in my database that all need to get filled up when I submit this form. The form matches each column with an input field of the same name (the name attribute on the input field is the same as the column name it belongs in. So $_POST['firstName'] goes in the firstName column, and so on).
Is there a way using mysqli or PDO that I could easily just take all my POST data and automatically insert it into the MySQL table without going through each field by hand? I could code them all out using prepared statements, but there are a ton of columns and I'd like to get them done all at once if possible.
This is the beginning of the long version I don't really want to have to complete.
$stmt = $connection->prepare("INSERT INTO area_retreat
(user,firstName,lastName,...etc)
VALUES
(?,?,?,...etc)
ON DUPLICATE KEY UPDATE
user=VALUES(user),
firstName=VALUES(firstName),
lastName=VALUES(lastName),
...etc
");
$stmt->bind_param("sss",
$username,
$_POST['firstName'],
$_POST['lastName']
);
$stmt->execute();
INSERT INTO area_retreat VALUES (?, ?, ...) -- however, you have to match ALL columns as shown in the database.
If you have an auto increment ID, you will need to provide NULL for that column in the proper column order.
To avoid errors you definitely need to store the list of variables one way or another. It could be as simple as an array:
$fields = array('firstName', etc.);
Then you can loop through your array to generate your sql statement dynamically and using named placeholders instead of question marks, you only need to bind them once. You can also store the values in an array and send that array as a parameter to execute():
// start of query
$values = array();
$query = '...';
foreach ($fields as $field)
{
if (isset($_POST[$field]))
{
// add to query
$query .= "...";
// add value to array so that you can feed the array to `execute`
$values[':' . $field] = $_POST[$field];
}
}
// add end of query
$query .= '...';
$stmt->execute($values);
If you want to use the same variables in an ON DUPLICATE KEY UPDATE section, you can do another loop or build an insert section that you can use twice after looping once.