I have the following function. I expect it to print the number of rows in the table provided in its argument.
private function getTotalCount($tbl){
$sql = "SELECT count(*) FROM :tbl ;";
$sth = $this->db->prepare($sql);
$sth->execute(array(
':tbl' => $tbl
));
$data = $sth->fetch(PDO::FETCH_ASSOC);
print_r($data);
}
But the function is not printing anything...
When I replace the function to something like this:
private function getTotalCount($tbl){
$sql = "SELECT count(*) FROM $tbl ;";
$sth = $this->db->prepare($sql);
$sth->execute();
$data = $sth->fetch(PDO::FETCH_ASSOC);
print_r($data);
}
Then it works fine and print the number of rows.
QUESTION: Why the execute() function not binding the :tbl parameter to $tbl ??
Sadly MySQL PDO doesn't accept parameters for SQL keywords, table names, view names and field names. This doesn't really come up in the main manual, but is mentioned a couple of times in comments.
The solution you have in the second piece of code is the workaround, although you may wish to sanitise the table name first (checking against a white list of table names would be ideal). More info: Can PHP PDO Statements accept the table or column name as parameter?
Related
Currently Im trying to create a PDO class where I will have a method to run a query like INSERT, UPDATE, or DELETE.
For examaple this is my method for a SELECT
public function getPreparedQuery($sql){
$stmt = $this->dbc->prepare($sql);
$stmt->execute([5]);
$arr = $stmt->fetchAll(PDO::FETCH_ASSOC);
if(!$arr) exit('No rows');
$stmt = null;
return $arr;
}
And i Simply call it like this:
$stmt = $database->getPreparedQuery($sql2);
var_export($stmt);
And so far I know that a runQuery should work something similar to this:
Insert Example without using a method:
$idRol = "6";
$nomRol = "test6";
$stmt = $database->dbc->prepare("insert into roles (idRol, NomRol) values (?, ?)");
$stmt->execute(array($idRol,$nomRol));
$stmt = null;
But I want to make it into an universal method where i simply can pass the sql sentence, something like this:
$database->runQuery($query);
but the query can happen to be
$query = "INSERT INTO roles (idRol, NomRol) VALUES ('4','test')";
or
$query = "INSERT INTO movies (movName, movLength, movArg) VALUES ('name1','15','movie about...')";
So how do I slice the arg $query so I can get all the variables that are being used in order to make an universal runQuery?
Because I can imagine that my method should be something like this
runQuery([$var1 , $var2, $var3....(there can be more)] , [$var1value, $var2value, $var3value...(there can be more)]){
$stmt = $database->dbc->prepare("insert into movies($var1 , $var2, $var3....(there can be more)) values (?, ? , ? (those are the values and there ca be more than 3)"); //how do i get those "?" value and amount of them, and the name of the table fields [movName, movLength, movArg can be variable depending of the sentence]?
$stmt->execute(array($var1,$var2, $var3, ...)); //this is wrong , i dont know how to execute it
$stmt = null;
}
You need to add a second parameter to your function. Simply an array where all those variables would go. An array by definition can have an arbitrary number of elements, which solves your problem exactly:
public function runQuery($sql, $parameters = []) {
$stmt = $this->dbc->prepare($sql);
$stmt->execute($parameters);
return $stmt;
}
this simple function will run ANY query. You can see the usage example in my article dedicated to PDO helper functions:
// getting the number of rows in the table
$count = $db->runQuery("SELECT count(*) FROM users")->fetchColumn();
// the user data based on email
$user = $db->runQuery("SELECT * FROM users WHERE email=?", [$email])->fetch();
// getting many rows from the table
$data = $db->runQuery("SELECT * FROM users WHERE salary > ?", [$salary])->fetchAll();
// getting the number of affected rows from DELETE/UPDATE/INSERT
$deleted = $db->runQuery("DELETE FROM users WHERE id=?", [$id])->rowCount();
// insert
$db->runQuery("INSERT INTO users VALUES (null, ?,?,?)", [$name, $email, $password]);
// named placeholders are also welcome though I find them a bit too verbose
$db->runQuery("UPDATE users SET name=:name WHERE id=:id", ['id'=>$id, 'name'=>$name]);
// using a sophisticated fetch mode, indexing the returned array by id
$indexed = $db->runQuery("SELECT id, name FROM users")->fetchAll(PDO::FETCH_KEY_PAIR);
As you can see, now your function can be used with any query with any number of parameters
This question already has an answer here:
PDO pagination with LIKE
(1 answer)
Closed 2 years ago.
I have one doubt about PDO.
I have a method in the class that returns data from the database for sent filters.
I want to get a number of rows for that query, but there are LIMIT and STAR in the query.
So because of that, I am using two queries to get a number of rows and data but to work, I need to bind the same value two times. Is there any more elegant way to achieve not have repeated code?
The method that I use is below.
$db = $this->openConnection();
$sql = " SELECT * FROM contacts";
// Filter data by main search input
if(!empty($search_query)){
$sql .= " WHERE ( location LIKE :search_query_location OR address LIKE :search_query_address ) ";
}
$sql .=" ORDER BY ".$order;
$stmt = $db->prepare($sql);
if(!empty($search_query)){
$stmt->bindValue(':search_query_location', (string) $search_query.'%');
$stmt->bindValue(':search_query_address', (string) $search_query.'%');
}
// Get number of rows after filter
$stmt->execute();
$total = $stmt->rowCount();
$sql .=" LIMIT :start, :limit_num";
$stmt = $db->prepare($sql);
if(!empty($search_query)){
$stmt->bindValue(':search_query_location', (string) $search_query.'%');
$stmt->bindValue(':search_query_address', (string) $search_query.'%');
}
// Bind start and limit value
$stmt->bindValue(':start', (int) $start, PDO::PARAM_INT);
$stmt->bindValue(':limit_num', (int) $limit, PDO::PARAM_INT);
// Get filtered data
$stmt->execute();
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
return array($total,$data);
WHY I NEED TO REPEAT BINDING FOR TWO SAME QUERIES ONE WITHOUT LIMITS TO WORK IS THERE ANY ELEGANT SOLUTION
Problem
The reason that you have to bind twice is that $pdo->prepare($sql) returns a PDOStatement which isn't editable after it's been set. So when you update it you have to overwrite it and start again... Obviously the new statement doesn't retain the old bound parameters.
If you think of it as an array that you add some data to and then overwrite with a new, blank, array... You then can't read the information from the original array because it doesn't exist in the new one:
$array = [];
$array[] = 1;
$array[] = 2;
$array[] = 3;
var_dump($array);
/*
Output...
Array
(
[0] => 1
[1] => 2
[2] => 3
)
*/
$array = [];
print_r($array);
/*
Output...
Array
(
)
*/
The difference is that PDOStatement is an object not an array. But it's functionally the same thing!
N.B.
While $pdo->rowCount() may return the number of results from a SELECT query it isn't guaranteed so usually it's best practice not to use it.
I wouldn't overwrite the variable with a new query anyway... Better to use a different variable name e.g. $countQuery and $dataQuery
Solutions
So, if the only reason is that you're trying to reduce the amount of code then there are a bunch of solutions that you could use. However, this doesn't appear to be code golf, so why does it matter?
Solution 1
Assuming you don't have an unreasonable amount of unneeded results returned by the query then you could just return the array from the first query and use array_slice to take the place of the second query...
$pdo = $this->openConnection();
$sql = "SELECT * FROM contacts";
if($search_query){
$sql .= " WHERE ( location LIKE :search_query_location OR address LIKE :search_query_address ) ";
}
$sql .= " ORDER BY :order";
$query = $pdo->prepare($sql);
if($search_query){
$query->bindValue(':search_query_location', $search_query.'%');
$query->bindValue(':search_query_address', $search_query.'%');
}
$query->bindValue(':order', $order);
$query->execute();
$result = $query->fetchAll(PDO::FETCH_ASSOC);
$count = count($result);
return [$count, array_slice($result, $start, $limit)];
Solution 2
If you're worried about readability and code maintenance then you should remember that: it's usual for a method/function to have a reasonably specific function, for example...
Return the number of rows which match a query
Return the data which matches a query
Implementing this would mean you have each of your queries in separate functions:
function countContacts(...)
{
$sql = 'SELECT count(*) FROM contacts WHERE ...';
$query = $pdo->prepare($sql);
$query->bindValue(...);
$query->execute();
return $query->fetchColumn();
}
function getContacts(...)
{
$sql = 'SELECT * FROM contacts WHERE ... ORDER BY ... LIMIT ...';
$query = $pdo->prepare($sql);
$query->bindValue(...);
$query->execute();
return $result->fetchAll(PDO::FETCH_ASSOC);
}
Solution 3
I wouldn't use this, but it technically solves the issue
You could use a union and run two queries in one, then you could use emulated prepared statements (as per #Straberry's answer) to bind once...
Although, again, emulated prepared statements are not something that anyone on here is likely to suggest you should use without good reason. Of course you could use normal prepares and use different bind parameter names.
Either way, this isn't a great solution. I wouldn't use it.
$sql = "
SELECT COUNT(*) as col1, null as col2, null as col3, null as col4, null as col5 FROM contacts WHERE ...
UNTION
SELECT col1, col2, col3, col4, col5 FROM contacts WHERE ... ORDER BY ... LIMIT ...
";
$query = $pdo->prepare($sql);
$query->bindValue(...);
$query->execute();
$result = $query->fetchAll(PDO::FETCH_ASSOC);
return [$result[0]["col1"], array_slice($result, 1)];
I am new to php and trying hard to learn its why you guys and gals need to Forgive me for asking a lot!
Here is my question;
I am trying to call a function with where clause multiple times, I have read allmost all posts and examples still didn't understand how to do it.
I tought that An example will be more useful than any blurb I can write.
Here is the function I am trying to create and use it multiple times :
function getTable($tableName, $clause) {
$stmt = $pdo->prepare("SELECT * FROM ".$tableName." WHERE ".$clause." = :".$clause);
$stmt->bindParam(":$clause", $clause, PDO::PARAM_STR);
$stmt->execute();
if($stmt->rowCount() > 0){
return true;
}else{
return false;
}
return $stmt;
}
I am not sure if my fucntion is safe or its rigth.
AND this is how I am trying to call function, which I dont know how to call table name and where clause and how to turn while loop.
getTable('posts');
If you give an example of creating and caling function, I would be grateful, Thanks
Nope, your function is not safe. Moreover it is just useless. There is no use case where you would use it like this getTable('posts');. And for the everything else it is much better to allow the full SQL syntax, not some limited subset.
The simplest yet most powerful PDO function I can think of is a function that accepts a PDO object, an SQL query, and array with input variables. A PDO statement is returned. I wrote about such function in my article about PDO helper functions. So here is the code:
function pdo($pdo, $sql, $args = NULL)
{
if (!$args)
{
return $pdo->query($sql);
}
$stmt = $pdo->prepare($sql);
$stmt->execute($args);
return $stmt;
}
With this function you will be able to run any query, with any number of WHERE conditions, and get results in many different formats. Here are some examples from the article mentioned above:
// getting the number of rows in the table
$count = pdo($pdo, "SELECT count(*) FROM users")->fetchColumn();
// the user data based on email
$user = pdo($pdo, "SELECT * FROM users WHERE email=?", [$email])->fetch();
// getting many rows from the table
$data = pdo($pdo, "SELECT * FROM users WHERE salary > ?", [$salary])->fetchAll();
// getting the number of affected rows from DELETE/UPDATE/INSERT
$deleted = pdo($pdo, "DELETE FROM users WHERE id=?", [$id])->rowCount();
// insert
pdo($pdo, "INSERT INTO users VALUES (null, ?,?,?)", [$name, $email, $password]);
// named placeholders are also welcome though I find them a bit too verbose
pdo($pdo, "UPDATE users SET name=:name WHERE id=:id", ['id'=>$id, 'name'=>$name]);
// using a sophisticated fetch mode, indexing the returned array by id
$indexed = pdo($pdo, "SELECT id, name FROM users")->fetchAll(PDO::FETCH_KEY_PAIR);
Special for you, here is the while example, though this method is considered clumsy and outdated:
$stmt = pdo($pdo,"SELECT * FROM tableName WHERE field = ?",[$value]);
while ($row = $stmt->fetch()) {
echo $row['name'];
}
Currently Im trying to create a PDO class where I will have a method to run a query like INSERT, UPDATE, or DELETE.
For examaple this is my method for a SELECT
public function getPreparedQuery($sql){
$stmt = $this->dbc->prepare($sql);
$stmt->execute([5]);
$arr = $stmt->fetchAll(PDO::FETCH_ASSOC);
if(!$arr) exit('No rows');
$stmt = null;
return $arr;
}
And i Simply call it like this:
$stmt = $database->getPreparedQuery($sql2);
var_export($stmt);
And so far I know that a runQuery should work something similar to this:
Insert Example without using a method:
$idRol = "6";
$nomRol = "test6";
$stmt = $database->dbc->prepare("insert into roles (idRol, NomRol) values (?, ?)");
$stmt->execute(array($idRol,$nomRol));
$stmt = null;
But I want to make it into an universal method where i simply can pass the sql sentence, something like this:
$database->runQuery($query);
but the query can happen to be
$query = "INSERT INTO roles (idRol, NomRol) VALUES ('4','test')";
or
$query = "INSERT INTO movies (movName, movLength, movArg) VALUES ('name1','15','movie about...')";
So how do I slice the arg $query so I can get all the variables that are being used in order to make an universal runQuery?
Because I can imagine that my method should be something like this
runQuery([$var1 , $var2, $var3....(there can be more)] , [$var1value, $var2value, $var3value...(there can be more)]){
$stmt = $database->dbc->prepare("insert into movies($var1 , $var2, $var3....(there can be more)) values (?, ? , ? (those are the values and there ca be more than 3)"); //how do i get those "?" value and amount of them, and the name of the table fields [movName, movLength, movArg can be variable depending of the sentence]?
$stmt->execute(array($var1,$var2, $var3, ...)); //this is wrong , i dont know how to execute it
$stmt = null;
}
You need to add a second parameter to your function. Simply an array where all those variables would go. An array by definition can have an arbitrary number of elements, which solves your problem exactly:
public function runQuery($sql, $parameters = []) {
$stmt = $this->dbc->prepare($sql);
$stmt->execute($parameters);
return $stmt;
}
this simple function will run ANY query. You can see the usage example in my article dedicated to PDO helper functions:
// getting the number of rows in the table
$count = $db->runQuery("SELECT count(*) FROM users")->fetchColumn();
// the user data based on email
$user = $db->runQuery("SELECT * FROM users WHERE email=?", [$email])->fetch();
// getting many rows from the table
$data = $db->runQuery("SELECT * FROM users WHERE salary > ?", [$salary])->fetchAll();
// getting the number of affected rows from DELETE/UPDATE/INSERT
$deleted = $db->runQuery("DELETE FROM users WHERE id=?", [$id])->rowCount();
// insert
$db->runQuery("INSERT INTO users VALUES (null, ?,?,?)", [$name, $email, $password]);
// named placeholders are also welcome though I find them a bit too verbose
$db->runQuery("UPDATE users SET name=:name WHERE id=:id", ['id'=>$id, 'name'=>$name]);
// using a sophisticated fetch mode, indexing the returned array by id
$indexed = $db->runQuery("SELECT id, name FROM users")->fetchAll(PDO::FETCH_KEY_PAIR);
As you can see, now your function can be used with any query with any number of parameters
I'm having some trouble using a variable declared in PHP with an SQL query. I have used the resources at How to include a PHP variable inside a MySQL insert statement but have had no luck with them. I realize this is prone to SQL injection and if someone wants to show me how to protect against that, I will gladly implement that. (I think by using mysql_real_escape_string but that may be deprecated?)
<?php
$q = 'Hospital_Name';
$query = "SELECT * FROM database.table WHERE field_name = 'hospital_name' AND value = '$q'";
$query_result = mysqli_query($conn, $query);
while ($row = mysqli_fetch_assoc($query_result)) {
echo $row['value'];
}
?>
I have tried switching '$q' with $q and that doesn't work. If I substitute the hospital name directly into the query, the SQL query and PHP output code works so I know that's not the problem unless for some reason it uses different logic with a variable when connecting to the database and executing the query.
Thank you in advance.
Edit: I'll go ahead and post more of my actual code instead of just the problem areas since unfortunately none of the answers provided have worked. I am trying to print out a "Case ID" that is the primary key tied to a patient. I am using a REDCap clinical database and their table structure is a little different than normal relational databases. My code is as follows:
<?php
$q = 'Hospital_Name';
$query = "SELECT * FROM database.table WHERE field_name = 'case_id' AND record in (SELECT distinct record FROM database.table WHERE field_name = 'hospital_name' AND value = '$q')";
$query_result = mysqli_query($conn, $query);
while ($row = mysqli_fetch_assoc($query_result)) {
echo $row['value'];
}
?>
I have tried substituting $q with '$q' and '".$q."' and none of those print out the case_id that I need. I also tried using the mysqli_stmt_* functions but they printed nothing but blank as well. Our server uses PHP version 5.3.3 if that is helpful.
Thanks again.
Do it like so
<?php
$q = 'mercy_west';
$query = "SELECT col1,col2,col3,col4 FROM database.table WHERE field_name = 'hospital_name' AND value = ?";
if($stmt = $db->query($query)){
$stmt->bind_param("s",$q); // s is for string, i for integer, number of these must match your ? marks in query. Then variable you're binding is the $q, Must match number of ? as well
$stmt->execute();
$stmt->bind_result($col1,$col2,$col3,$col4); // Can initialize these above with $col1 = "", but these bind what you're selecting. If you select 5 times, must have 5 variables, and they go in in order. select id,name, bind_result($id,name)
$stmt->store_result();
while($stmt->fetch()){ // fetch the results
echo $col1;
}
$stmt->close();
}
?>
Yes mysql_real_escape_string() is deprecated.
One solution, as hinted by answers like this one in that post you included a link to, is to use prepared statements. MySQLi and PDO both support binding parameters with prepared statements.
To continue using the mysqli_* functions, use:
mysqli_prepare() to get a prepared statement
mysqli_stmt_bind_param() to bind the parameter (e.g. for the WHERE condition value='$q')
mysqli_stmt_execute() to execute the statement
mysqli_stmt_bind_result() to send the output to a variable.
<?php
$q = 'Hospital_Name';
$query = "SELECT value FROM database.table WHERE field_name = 'hospital_name' AND value = ?";
$statement = mysqli_prepare($conn, $query);
//Bind parameter for $q; substituted for first ? in $query
//first parameter: 's' -> string
mysqli_stmt_bind_param($statement, 's', $q);
//execute the statement
mysqli_stmt_execute($statement);
//bind an output variable
mysqli_stmt_bind_result($stmt, $value);
while ( mysqli_stmt_fetch($stmt)) {
echo $value; //print the value from each returned row
}
If you consider using PDO, look at bindparam(). You will need to determine the parameters for the PDO constructor but then can use it to get prepared statements with the prepare() method.