Table Items:
Source
$query = "SELECT * FROM `tbl_items` WHERE `name` LIKE '%cell%' AND `is_active`=1";
// the query is generated dynamically so i do have a dynamic params too
$params = [
'name' => '%cell%',
'is_active' => 1,
];
$prepared = $db->prepare($query);
foreach($params as $key => $val)
{
if(is_int($val))
{
$prepared->bindParam(':'.$key, $val, PDO::PARAM_INT);
}
else
{
$prepared->bindParam(':'.$key, $val);
}
}
upon execution it returns empty result ..
Not Working:
multiple parameters .. (except i manually bind the parameters)
$prepared->bindParam(':name', $params['name']);
$prepared->bindParam(':is_active', $params['is_active']);
Working:
single parameter .. (it returns exactly what i needed)
Now my question, what could be the reason why it returns empty when looping the bindParam() ?
bindParam takes its value by reference. Meaning, at the time you do execute(), it takes the then current value of whatever is assigned to $val. Which obviously is probably not what you expect at the end of the loop.
Use bindValue instead, which binds the value immediately, instead of a variable reference.
Related
I'm trying to incorporate a WHERE IN statement into an already-working query using PDO. I've gone through and dynamically created positional placeholders using the count of an indexed array, and looped through said array to bind each value. However when this is all said and done I'm only getting one result, specifically a result matching the last value I dynamically bound.
To add to this, if I simply implode $arrayToCheck with a comma as a delimiter in place of $placeholderSpots, I get the full 24 results I'm expecting.
$arrayToCheck = json_decode($listFromDatabase); //This is a Python List converted to a string using Python's json.dumps, which was then passed into a database table.
$timePeriod = 0;
$rowLimit = 2500;
$placeholderSpots = implode(", ", array_fill(0, count($arrayToCheck), "?"));
$toPull = $GLOBALS['MainDatabase']->prepare("SELECT * FROM datatable WHERE timecolumn >= ? AND idcolumn IN ($placeholderSpots) ORDER BY timecolumn DESC LIMIT ?");
$toPull->bindParam(1, $timePeriod, PDO::PARAM_INT);
$tempCounter = 2;
foreach ($arrayToCheck as $eachID) {
$toPull->bindParam($tempCounter, $eachID, PDO::PARAM_INT);
$tempCounter++;
}
$toPull->bindParam($tempCounter, $rowLimit, PDO::PARAM_INT);
if ($toPull->execute()) {
while ($pulledData = $toPull->fetch(PDO::FETCH_ASSOC)) {
//Data Is Processed Here
}
}
You need to replace bindParam() with bindValue().
foreach ($arrayToCheck as $eachID) {
$toPull->bindValue($tempCounter++, $eachID, PDO::PARAM_INT);
}
The bindParam() method bind by reference to the variable. You assign a new value each time to the same variable when looping over the array, so the variable doesn't change, only the value does. The end result is that you have bound the same variable multiple times that holds the last value from the array.
This is my function:
function countRows($sql, $parameters){
$db = new PDO("mysql:host=localhost;dbname=cms","root");
$result = $db->prepare($sql);
foreach ($parameters as $key => $parameter) {
$result->bindParam($key, $parameter);
}
$result->execute();
$number_of_rows = $result->fetchAll();
return count($number_of_rows);
}
With $parameters array like this it works just fine:
$parameters=array("key"=>"parameter");
But when the array has more keys and variables it just will give result 0
For example with array like this it gives me 0 rows, when it should be 3
$parameters=array("key"=>"parameter", "key2"=>"parameter2");
Edit:
Example of an query:
"SELECT * FROM users WHERE username=:username AND password=:password"
Example of an $parameters for this query:
$parameters = array(":username"=>$username, ":password"=>$password);
When i run it with one column (like only username or only password) it runs fine, with both it returns always 0.
The problem is that you're using bindParam() for a variable that changes its value:
foreach ($parameters as $key => $parameter) {
$result->bindParam($key, $parameter);
}
You have to understand that bindParam() binds the variable by reference, not the value the variable has at the time you do the bind.
You're binding the variable $parameter to both parameters in your query. The value isn't sent until you call execute(). So it sends the value that the variable has after the loop is finished, which is the password.
I tested this by enabling the general query log on my MySQL server, and observing what gets logged:
140905 10:09:45 49 Connect root#192.168.56.1 on test
49 Prepare SELECT * FROM users WHERE username=? AND password=?
49 Execute SELECT * FROM users WHERE username='password' AND password='password'
49 Close stmt
49 Quit
You can solve this in either of two ways:
Use bindValue() instead of bindParam(). This copies the value of $parameter at the time you do the bind.
Alternatively, you don't need to do a loop for binding parameters one at a time all, just pass the array to execute():
$result->execute($parameters);
I don't know why so many developers believe they're required to write code to bind parameters one at a time. It's easier to pass the array of parameters directly to execute().
Your parameters should look like array
$parameters = [
"key" => "parameter",
"key2 " => "parameter2"
];
Then You missed the password
$db = new PDO("mysql:host=localhost;dbname=cms","root","your_password");
My idea take a look on PDOStatement::bindParam Docs
I have a table of contacts with the following fields/data types: contactid (PK, auto-increment, not null), fname varchar(50), lname varchar(50), email varchar(50), is_member tinyint(1) (i.e., boolean), and createdate date.
The data is submitted to the same PHP page that the form is on, and the values of the $_POST array are packaged into a Contact object and passed to the following function:
<?php
public static function insertContact(Model $model, Contact $contact) {
$database = new Database();
$sql = 'INSERT INTO contact (fname, lname, email, is_member, createdate) VALUES (:fname, :lname, :email, :is_member, CURDATE())';
$params = array(
':fname' => $contact->getFirstname(),
':lname' => $contact->getLastname(),
':email' => $contact->getEmail(),
':is_member' => intval($contact->isMember())
);
$result = $database->query($sql, $params, 'LASTID'); // Will return last inserted ID.
$model->notify('Added contact ID ' . strval($result) . ' to the database.');
}
?>
This function is called if another function that checks to see if the contact already exists returns false. I'm using PDO prepared statements, and everything's working fine for SELECT statements, but when I try to execute this INSERT statement, I'm running into problems. The $params array is correct, and the number of params matches the number of placeholders, but the is_member field is causing problems. If the value is 1, the INSERT completes, but the resulting row looks something like this:
contactid fname lname email is_member createdate
14 1 1 1 1 2014-05-31
Has anybody seen this behavior before? Moreover, if the value of is_member is 0, then the query fails entirely with the PDOStatement warning [HY093]: Invalid parameter number: number of bound variables does not match number of tokens. What I don't get is that $params has the correct number of values to bind to the placeholder tokens.
[UPDATE:] The $database->query() method is as follows:
<?php
// Return type is the format in which to return the result of the query.
public function query($sql, $params, $returnType) {
if($stmt = $this->connection->prepare($sql)) {
foreach($params as $key => $value) {
if($value != 'noParams') {
$stmt->bindParam($key, $value);
}
}
if($stmt->execute()) { // execute() returns TRUE on success
switch($returnType) {
case 'ASSOC':
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
break;
case 'LASTID':
$result = $this->connection->lastInsertId();
break;
// ... more cases, etc.
}
} else {
$result = null;
die('Error: ' . $this->connection->error);
}
return $result;
} else {
die('Prepare failed: (' . $this->connection->error . ')');
}
}
?>
As mentioned, this query() method works for prepared statements for SELECT operations, however it is exhibiting the aforementioned behavior when trying to do a simple INSERT. Any help is appreciated!! Thanks!
The first problem:
if($value != 'noParams') {
If value is 0, then this will be a false match because it's a loose-typed comparison; so "falsey" type values (null, 0.0, false, etc) will give you problems, because it won't bind any falsey values from your array... which explains your Invalid parameter number: number of bound variables does not match number of tokens error....
make it a strict comparison:
if($value !== 'noParams') {
Your second problem:
Quoting from the PHP Docs
Unlike PDOStatement::bindValue(), the variable is bound as a reference and will only be evaluated at the time that PDOStatement::execute() is called.
At the point where you actually execute the statement, $value only has the value from the last iteration of your binding loop, which is the value from the is_member array element, which is 1. That's why you're getting nothing but 1 values for all your bind variables
Change your foreach loop to
foreach($params as $key => &$value) {
So that $value is "by reference", and it should then be the correct reference that is bound to each bind var
Alternatively, use bindValue() rather than bindParam()
Holistic:
So for the complete fix
foreach($params as $key => &$value) {
if($value !== 'noParams') {
$stmt->bindParam($key, $value);
}
}
have you tried using exec versus query?
in the docs:
http://us1.php.net/manual/en/pdo.exec.php
versus
http://us1.php.net/pdo.query
bottom line: PDO::query — Executes an SQL statement, returning a result set as a PDOStatement object
while PDO::exec — Execute an SQL statement and return the number of affected rows
I'm trying to figure out how to convert my history script from mysql_query() to PDO. I have a form with 4 input fields which you can randomly select. Which means there can be 0, 1, 2, 3, 4 fields selected depending on which info you're trying to get.
I've tried to query db like this:
$q = $db->prepare('SELECT date,
name,
action
FROM history
WHERE name = :name
AND action = :action');
$q->bindParam(':name', $Name, PDO::PARAM_STR, 20);
$q->bindParam(':action', $Action, $PDO::PARAM_STR, 20);
$q->execute();
But this doesn't work if I don't have any fields selected and want the whole history shown.
With mysql_query() I'd just do this:
mysql_query('SELECT date,
name,
action
FROM history
$Name
$Action');
Which means if there's no $Name or $Action they're simply not included in the query.
Should I just copy/paste the old query into $q = $db-query('')? But that kind of defeats the purpose of using PDO.
You could always assign default values to the params which conform to the column names.
That way your query will in the default case end up as where column = column and when there is a value present it will be where column = value.
EDIT:
Of course, my logic was slightly flawed, since bindParam does not work that way. Instead, you should incrementally build your statement according to the params set.
/* Start with the most general case for the sql query.
* The where part always evaluates to true and will thus
* always return all rows and exists only to make appending
* further conditions easier.
*/
$q = 'SELECT date, name, action FROM history WHERE 1';
/* Prepare a params array in any way you wish. A loop might be more
* efficient if it is possible, but since in this example you have
* only 2 variables, it didn't seem necessary
*/
$params = array();
if (! empty($Name)) {
$params['name'] = $Name;
}
if (! empty($Action)) {
$params['action'] = $Action;
}
/* When the params array is populated, complete the sql statement by
* appending the param names joined with ANDs
*/
foreach ($params as $key => $value) {
$q .= sprintf(' AND `%s` = :%s', $key, $key);
}
/* When the query is complete, we can prepare it */
$stmt = $db->prepare($q);
/* Then bind the values to the prepared statement
*/
foreach ($params as $key => $value) {
// Using bindValue because bindParam binds a reference, which is
// only evaluated at the point of execute
$stmt->bindValue(':'.$key, $value);
}
/* Now we're ready to execute */
$stmt->execute();
In this example, the empty check could've been done in the loop where we complete the sql statement, but that would've given you a less general example.
This example also leaves out the type param to bindValue, but that would be easily implemented, e.g. by changing the array value to an object or array having the type as a member, or by duck typing inside the assigning loop.
The query building could in this form easily be put in a function that would work for all your database querying needs, as long as you provide it the initial (general case) query along with the params array.
I modified this code from somewhere but I am not sure if I am doing it correctly,
I use method to insert data into database,
# insert or update data
public function query($query, $params=array())
{
try
{
$stmt = $this->connection->prepare($query);
$params = is_array($params) ? $params : array($params);
$stmt->execute($params);
return true;
}
catch (PDOException $e)
{
# call the get_error function
$this->get_error($e);
}
}
Then I just need to call it like this,
$sql = "
INSERT root_countries_cities_towns (
tcc_names,
cny_numberic,
tcc_created
)VALUES(
?,
?,
NOW()
)";
$pdo->query($sql,array('UK','000'));
It works fine perfectly! but I don't understand what this line does - can someone explain please?
$params = is_array($params) ? $params : array($params);
I thought I have to use bindParam to bind the parameters first, but it seems that I don;t have to anymore with is method - is it safe and secure then??
Does it meant that I don't have to prepare the query in this way anymore?
$sql = "
INSERT root_countries_cities_towns (
tcc_names,
cny_numberic,
tcc_created
)VALUES(
:name,
:numberic,
NOW()
)";
and forget about this binding?
$stmt = bindParam(':name','UK', PDO::PARAM_STR);
$stmt = bindParam(':numberic','000', PDO::PARAM_STR);
Thanks.
I guess that's pretty much PHP syntax question rather than PDO one.
$params = is_array($params) ? $params : array($params);
is a shortland (called ternary operator)) for
if (is_array($params)) {
$params = $params;
} else {
$params = array($params);
}
which I'd rather wrote as
if (!is_array($params)) $params = array($params);
which is pretty self-explanatory and can be read almost in plain English:
if $params is not an array, let's make it array with one value of former $params
That's why I hate ternary operator (and lambdas) and always avoid it's use. It makes pretty readable code into a mess. Just out of programmer's laziness.
To answer your other questions,
Does it meant that I don't have to prepare the query in this way anymore?
Who said that? You're preparing it all right in your code, check it again.
and forget about this binding?
that's true. execute($params) is just another way to bind variables.
the line
$params = is_array($params) ? $params : array($params);
is simply checking if the $params variable is an array, and if so, it creates an array with the original $params value as its only element, and assigns the array to $params.
This would allow you to provide a single variable to the query method, or an array of variables if the query has multiple placeholders.
The reason it doesn't use bindParam is because the values are being passed to the execute() method. With PDO you have multiple methods available for binding data to placeholders:
bindParam
bindValue
execute($values)
The big advantage for the bindParam method is if you are looping over an array of data, you can call bindParam once, to bind the placeholder to a specific variable name (even if that variable isn't defined yet) and it will get the current value of the specified variable each time the statement is executed.
The first example transforms the contents of your $params into an array, if it wasn't already an array (for example if only one parameter was passed and it was passed as an individual item instead of as an array of length 1).
The two examples work just as well, except that for the first one, parameters introduced with the array $params are injected where ? are found in the SQL query, whereas in the second one, the formatting of the parameters is actually done by name (you bind a parameter name as found in the sql to an actual parameter).
You should use the first one, it's easier to write.
Passing an array to PDOStatement::execute() passes each entry in the array through PDOStatement::bindParam() (or maybe bindValues()) using defaults (bind type, etc).
Basically, it's as safe as pre-binding.
The advantage to using bindParam is that it binds to the variable reference. This means you can change the value of the variable without re-binding and execute the statement with new values. This is especially useful in a loop, eg
$vals = array('foo', 'bar', 'baz');
$stmt->bindParam(1, $val);
foreach ($vals as $val) {
$stmt->execute(); // Executes once for each value in $vals
}
I don't understand what this line does - can someone explain please?
That line converts a non-array into an array.
For example
$params = 'foo';
$params = is_array($params) ? $params : array($params);
$params == array('foo');
I imagine it's to facilitate situations where you have only one placeholder and one value to bind as PDOStatement::execute() can only be passed an array.
Try this class. I use PDO a lot and this is what I use all the time for my projects.PHP PDO Class on GitHub