Is there an easy way to echo the value stored in a bound parameter.
$sql ="call storedproc(:firstname, :lastname)";
$stmt = $this->DBH->prepare($sql);
$stmt->bindParam(':firstname', $fname);
$stmt->bindParam(':lastname', $lname);
//I want to do this
echo $stmt->firstname;
$stmt->execute;
If you only want to "see" what's going on then there's PDOStatement->debugDumpParams():
Dumps the informations contained by a prepared statement directly on the output. It will provide the SQL query in use, the number of parameters used (Params), the list of parameters, with their name, type (paramtype) as an integer, their key name or position, the value, and the position in the query (if this is supported by the PDO driver, otherwise, it will be -1).
<?php
function parms($string,$data) {
$indexed=$data==array_values($data);
foreach($data as $k=>$v) {
if(is_string($v)) $v="'$v'";
if($indexed) $string=preg_replace('/\?/',$v,$string,1);
else $string=str_replace(":$k",$v,$string);
}
return $string;
}
$string='INSERT INTO stuff(name,value) VALUES (?,?)';
$data=array('Fred',23);
$string='INSERT INTO stuff(name,value) VALUES (:name,:value)';
$data=array('name'=>'Fred','value'=>23);
print parms($string,$data);
$update = "update registration";
$update .= " set status=:statusid";
$update .= " where refid=:refid";
$stmt = $db->prepare($update);
$data=array('statusid' => $statusid,'refid' => $refid);
echo parms($update,$data); // checking bind values
exit;
$stmt->execute(array ('statusid' => $statusid,'refid' => $refid));
?>
//output
update registration set status='all' where refid='45'
Related
This question already has answers here:
How to bind mysqli bind_param arguments dynamically in PHP?
(8 answers)
Closed 2 years ago.
I tried multiple ways to create a function to bind dynamic array values into the MySQLi prepared statements. But I am getting error 'Uncaught mysqli_sql_exception: No data supplied for parameters in prepared statement'
Here is my code:
if (count($fields) == count($values)) {
$fielddata = implode(", ", $fields);
$questions = rtrim(str_repeat("?, ", count($values)), ", ");
$typedata = implode("", $type);
foreach ($values as $index => $current_val){ // build type string and parameters
$value .= '$values['.$index.'],';
}
$value = rtrim($value,',');
$statement = "INSERT INTO ".$table." (".$fielddata.") VALUES (".$questions.")";
$stmt = $db->prepare($statement);
$stmt->bind_param("sss", $value);
$stmt->execute();
$stmt->close();
echo "inserted";
}
The same code works when I replace
$stmt->bind_param("sss", $value);
with
$stmt->bind_param("sss",$values[0],$values[1],$values[2]);
bind_param() doesn't take a string that is a comma separated list of values, which seems to be what you are trying to pass it.
Move your foreach loop below prepare() and bind the values inside the loop.
if (count($fields) == count($values)) {
$fielddata = implode(", ", $fields);
$questions = rtrim(str_repeat("?, ", count($values)), ", ");
$typedata = implode("", $type);
//NOTE: You should verify that `$table` contains a valid table name.
$statement = "INSERT INTO {$table} ({$fielddata}) VALUES ({$questions})";
$stmt = $db->prepare($statement);
//bind parameters using variable unpacking (PHP 5.6+), assuming `$typedata` actually contains the proper types.
$stmt->bind_param($typedata, ...$values);
$stmt->execute();
$stmt->close();
echo "inserted";
}
You seem to be binding a single string as a second argument in your bind_param(). This method takes a number of variables by reference and binds them to the placeholders in the query and since you bound a single string the number of bound parameters does not match.
You need to store the values in an array and then unpack them using the splat operator.
if (count($fields) == count($values)) {
$fielddata = implode(", ", $fields);
$questions = rtrim(str_repeat("?, ", count($values)), ", ");
$statement = "INSERT INTO ".$table." (".$fielddata.") VALUES (".$questions.")";
$stmt = $db->prepare($statement);
$stmt->bind_param(str_repeat("s", count($values)), ...$values);
$stmt->execute();
}
Also, the type should be a list of letters denoting the type of each variable being bound. The best case is to bind them all as strings, so just repeat s for each bound variable.
Take care of SQL injection. You need to make sure that the field names are properly whitelisted. If these can be arbitrary values you could be vulnerable to SQL injection.
I have some method to insert some data into a database like this:
public function register($username, $email, $hashedPassword, $activationCode)
{
try {
$conn = Database::getConnection();
// Connect and create the PDO object
$conn->exec('SET CHARACTER SET utf8'); // Sets encoding UTF-8
// Define and prepare an INSERT statement
$sql = 'INSERT INTO users (username, email, pass, reset_token, dateAdded )
VALUES (:username, :pass, :email, :token, now())';
$sqlprep = $conn->prepare($sql);
// Adds value with bindParam
$sqlprep->bindParam(':username', $username, PDO::PARAM_STR);
$sqlprep->bindParam(':email', $email, PDO::PARAM_STR);
$sqlprep->bindParam(':pass', $hashedPassword);
$sqlprep->bindParam(':token', $activationCode);
// If the query is successfully executed, output the value of the last insert id
if ($sqlprep->execute()) {
//echo 'Succesfully added the row with id='. $conn->lastInsertId();
$this->result = true;
}
$conn = null; // Disconnect
} catch (PDOException $e) {
include('../views/error.php');
include('../views/admin/includes/footer.php');
exit();
}
}
The problem is I think it's not a good method if I have so many arguments for my function to enter into a database. So is it any good way I can enter a lot of fields just by using 1 parameter but still using bindParam? Since I see a lot of examples is only using prepare without bindParam. I think I can use an array, but I don't know the proper way to do it. So I need some help how I can do it.
since you want keep your bindparam i suggest you use input like this:
$input = array('username' => $username, 'activationHash' => $activationHash);
and in your bindParam add a code like this:
public function register($input){
//code
$sqlprep->bindParam(':username', $input['username'], PDO::PARAM_STR);
//other
}
hope this will solve your problem
https://stackoverflow.com/a/10060755/1747411
Check second example, you have to repeat values with binds
e.g
VALUES (:username1, :pass1, :email1, :token1, now()), (:username2, :pass2, :email2, :token2, now())
and bindParam with loop
You can insert the params as an array into $sqlprep->execute($param_array)
Or, simply passing each param into an array inside execute, like this: $sqlprep->execute(array($param1, $param2))
Update:
Pass values into $input as an array:
$input = array('username' => $username, 'activationHash' => $activationHash); //and so on
Now on the model side,
You can bind these values to params using foreach loop like this:
foreach ($values as $key => $value) {
$sqlprep->bindParam(':' . $key, $value , PDO::PARAM_STR);
}
I am looking to do multiple inserts using PHP PDO.
The closest answer I have found is this one
how-to-insert-an-array-into-a-single-mysql-prepared-statement
However the example thats been given uses ?? instead of real placeholders.
I have looked at the examples on the PHP doc site for place holders
php.net pdo.prepared-statements
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
Now lets say I wanted to achieve the above but with an array
$valuesToInsert = array(
0 => array('name' => 'Robert', 'value' => 'some value'),
1 => array('name' -> 'Louise', 'value' => 'another value')
);
How would I go about it with PDO and multiple inserts per transaction?
I imagine it would start of with a loop?
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
foreach($valuesToInsert as $insertRow){
// now loop through each inner array to match binded values
foreach($insertRow as $column => value){
$stmt->bindParam(":{$column}", value);
}
}
$stmt->execute();
However the above does not work but hopefully will demonstrate what im trying to achieve
First of all, ? symbols are real place-holders (most drivers allow to use both syntaxes, positional and named place-holders). Secondly, prepared statements are nothing but a tool to inject raw input into SQL statements—the syntax of the SQL statement itself is unaffected. You already have all the elements you need:
How to insert multiple rows with a single query
How to generate SQL dynamically
How to use prepared statements with named place-holders.
It's fairly trivial to combine them all:
$sql = 'INSERT INTO table (memberID, programID) VALUES ';
$insertQuery = [];
$insertData = [];
$n = 0;
foreach ($data as $row) {
$insertQuery[] = '(:memberID' . $n . ', :programID' . $n . ')';
$insertData['memberID' . $n] = $memberid;
$insertData['programID' . $n] = $row;
$n++;
}
if (!empty($insertQuery)) {
$sql .= implode(', ', $insertQuery);
$stmt = $db->prepare($sql);
$stmt->execute($insertData);
}
I'm assuming you are using InnoDB so this answer is only valid for that engine (or any other transaction-capable engine, meaning MyISAM isn't included).
By default InnoDB runs in auto-commit mode. That means each query is treated as its own contained transaction.
To translate that to something us mortals can understand, it means that every INSERT query you issue will force hard-disk to commit it by confirming it wrote down the query information.
Considering how mechanical hard-disks are super slow since their input-output operation per second is low (if I'm not mistaken, the average is 300ish IO's), it means your 50 000 queries will be - well, super slow.
So what do you do? You commit all of your 50k queries in a single transaction. It might not be the best solution for various purposes but it'll be fast.
You do it like this:
$dbh->beginTransaction();
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
foreach($valuesToInsert as $insertRow)
{
// now loop through each inner array to match bound values
foreach($insertRow as $column => value)
{
$stmt->bindParam(":$column", value);
$stmt->execute();
}
}
$dbh->commit();
A little modifications in solution provided by N.B
$stmt->execute() should be outside of inner loop because you may have one or more columns that need to bind before calling $stmt->execute() else you 'll get exception "Invalid parameter number: number of bound variables does not match number of token".
2nd "value" variable were missing dollar signs.
function batchinsert($sql,$params){
try {
db->beginTransaction();
$stmt = db->prepare($sql);
foreach($params as $row)
{
// now loop through each inner array to match bound values
foreach($row as $column => $value)
{
$stmt->bindParam(":$column", $value);
}
$stmt->execute();
}
db->commit();
} catch(PDOExecption $e) {
$db->rollback();
}
}
Test:
$sql = "INSERT INTO `test`(`name`, `value`) VALUES (:name, :value)" ;
$data = array();
array_push($data, array('name'=>'Name1','value'=>'Value1'));
array_push($data, array('name'=>'Name2','value'=>'Value2'));
array_push($data, array('name'=>'Name3','value'=>'Value3'));
array_push($data, array('name'=>'Name4','value'=>'Value4'));
array_push($data, array('name'=>'Name5','value'=>'Value5'));
batchinsert($sql,$data);
Your code was actually ok, but had a problem in $stmt->bindParam(":$column", value); It should be $stmt->bindValue(":{$column}", $value); and it will work perfectly. This will assist others in future.
Full code:
foreach($params as $row)
{
// now loop through each inner array to match bound values
foreach($row as $column => $value)
{
$stmt->bindValue(":{$column}", $value); //EDIT
}
// Execute statement to add to transaction
$stmt->execute();
}
Move execute inside of the loop.
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
foreach($valuesToInsert as $insertRow)
{
$stmt->execute($insertRow);
}
If you experience any problems with this such recommended way, you have to ask a question, describing these certain problems.
As I am writing a script, I am typing $db->prepare()'s and $stmt->bindParam()'s constantly. I am looking for a way to consolidate it all in a function. This is what I have so far.
$sql = "SELECT (name, email) FROM users WHERE VALUES (:name, :email)"
$values = array(':name' => 'my_name', ':email' => 'blahblah#example.com', );
function db_query($sql, $values) {
global $db; //Database object
$stmt = $db->prepare($sql);
foreach($values as $placeholder => $value) {
$stmt->bindParam($placeholder, $value);
}
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
Would this be sufficient for most queries? Is this a secure way to do this?
I just want the query to run and return whatever it returns (NULL, values, error, etc...).
Thanks.
Your code will not work as expected because bindParam() binds the placeholder name (first argument) to the variable reference present in the second argument.
Using your example, this would result in all parameters set to blahblah#example.com as it is the last $value in the loop.
As mentioned in the comments, simply use $stmt->execute($values). See http://php.net/manual/en/pdostatement.execute.php
If you really want to continue with your loop, use PDOStatement::bindValue() instead.
please help , this is not inserting into db
$dbh = new PDO('mysql:host=localhost;dbname=blog', root, root);
if($dbh){
// use the connection here
$stmt = $dbh->prepare("INSERT INTO comments (blog_id,dateposted,name,comment) VALUES (:blog_id,:dateposted,:name,:comment)");
$stmt->bindParam(':blog_id', $validentry);
$stmt->bindParam(':dateposted', NOW());
$stmt->bindParam(':name', $_POST['name']);
$stmt->bindParam(':comment', $_POST['comment']);
$stmt->execute();
// and now we're done; close it
}else{
echo mysql_error();
}
$dbh = null;
//redirect after posting
$stmt->bindParam(':dateposted', NOW());
PDOStatement::bindParam() binds the parameter to a PHP variable reference. As such, it requires the second argument to be a variable.
You can instead use PDOStatement::bindValue() to use a literal or return value from a function.
Also, NOW() is not a PHP function and as such, cannot be used here. If you're just wanting to use the DB function, hard-code it into the statement, eg
INSERT INTO comments (blog_id,dateposted,name,comment)
VALUES (:blog_id, NOW(), :name, :comment)
change NOW() to date('Y-m-d H:i:s')
Phil, ok, but if you need the bindParam to assign a value depending a condition?
In my case, I want to let the user define the creation date if he wants to.
$stmt = $conn->prepare('INSERT INTO news (title_fr, content_fr, creation_date) VALUES (:title_fr, :content_fr, :creation_date)');
if( $date ) {
$stmt->bindParam(':creation_date', $date);
} else {
$stmt->bindParam(':creation_date', NOW());
}