How to sort by time with prepared statements? - php

I recently managed to stumble across a problem with PDO prepared statements in PHP. I am trying to fetch some results from a MySQL table and sort them by the time entered in a column called start_time.
Here's the code that I'm running now:
class DatabaseTable {
private $table;
private $pdo;
public function __construct($pdo, $table) {
$this->pdo = $pdo;
$this->table = $table;
}
public function findMany2ordered($field, $value, $field2, $value2, $order, $direction) {
$stmt = $this->pdo->prepare('SELECT * FROM ' . $this->table . ' WHERE '.$field.' = :value AND '.$field2.' = :value2 ORDER BY :order :direction' );
$criteria = [
'value' => $value,
'value2' =>$value2,
'order' =>$order,
'direction' =>$direction
];
//$stmt->execute($criteria);
//return $stmt->fetch();
if($stmt->execute($criteria)){
return $stmt->fetchAll();
}else{
return $stmt->errorInfo()[2];
}
}
}
After this, I instantiate the class:
$slots = new DatabaseTable($pdo, 'slots');
and then I try and query the values and sort them by the time:
$timeline = $slots->findMany2ordered('user_id', $_SESSION['user_id'], 'slot_date', $_POST['timelinedate'], 'start_time', 'ASC');
then I have a foreach loop that iterates throught them and echo the results on the page:
foreach ($timeline as $slot){
$taskDetails = $tasks->find('task_id', $slot['task_id']);
//var_dump($taskDetails);
echo'<div class="slot"';
echo'<h3>'. $taskDetails['task_title']. '<h3>';
echo'<span> start time:'.$slot['start_time'].' </span>';
echo'</div>';
}
The results are still unordered while printed on the page:
Has anyone stumbled across this before? Is there a solution?
Thanks in advance for you help.

Since PDO parameters can't be used for table and column names, it would be best to write your query string in such a way that table / column names and sort order are specified as PHP variables and any literals that may be used for the values are used as placeholders / bounded parameters.
Therefore, your function would look something like:
public function findMany2ordered($field, $value, $field2, $value2, $order, $direction) {
$stmt = $this->pdo->prepare('SELECT * FROM ' . $this->table
. ' WHERE '.$field.' = :value '
. ' AND '.$field2.' = :value2 '
. ' ORDER BY '.$order .' '. $direction );
$criteria = [
':value' => $value,
':value2' => $value2
];
if($stmt->execute($criteria)){
return $stmt->fetchAll();
}else{
return $stmt->errorInfo()[2];
}
}

Related

PDO - executed query with binded parameters yields no results?

I am trying to create a PHP array of random "fruits" from a database.
The database class that I am using:
class Db
{
private static $_instance = null;
private $_pdo;
private function __construct()
{
try {
$this->_pdo = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME .'', DB_USER, DB_PASS);
} catch (PDOException $e) {
die($e->getMessage());
}
}
public static function getInstance()
{
if (!isset(self::$_instance)) {
self::$_instance = new Db();
}
return self::$_instance;
}
public function prepare($sql)
{
return $this->_pdo->prepare($sql);
}
}
The class that is using the database to fetch "fruits" to create an array of a given size of random entries by using 3 seperate queries to calculate and retrieve "x" number of random items form the database.
class FruitBasket
{
private $_fruitArray = array(),
$_inputCode,
$_db;
public function __construct($input = null)
{
$this->_inputCode = $input;
$this->_db = Db::getInstance();
var_dump($this->_db);
}
public function pickFruit($count)
{
$doubleCount = $count * 2;//double the count used in calculation with the random number
$fruitIDs = ''; //the choosen fruits (id's)
$i = 0;
//#1 get total count of fruits table
$sql = "SELECT COUNT(*) FROM `fruits`";
if ($query = $this->_db->prepare($sql)) {
if ($query->execute()) {
$allFruits = $query->fetch(PDO::FETCH_NUM);
} else {
print_r("ERROR QUERY DID NOT EXECUTE #1");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #1");
}
//#2 calculate random number to pull from all of id's
$sql = "SELECT id FROM `fruits` WHERE RAND()* ? < ? ORDER BY RAND() LIMIT 0, ? ";
if ($query = $this->_db->prepare($sql)) {
$query->bindParam(1, $allFruits[0], PDO::PARAM_INT);
$query->bindParam(2, $doubleCount, PDO::PARAM_INT);
$query->bindParam(3, $count, PDO::PARAM_INT);
if ($query->execute()) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
if ($i == 0) {
$fruitIDs .= "'" . $row['id'] . "'";
} else {
$fruitIDs .= ", '" . $row['id'] . "'";
}
$i++;
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #2");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #2");
}
//#3 get the fruits
$sql="SELECT NAME FROM `fruits` WHERE `id` IN( ? )";
if ($query = $this->_db->prepare($sql)) {
$query->bindParam(1, $fruitIDs, PDO::PARAM_STR);
if ($query->execute()) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
$this->_fruitArray[] = $row['name'];
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #3");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #3");
}
return $this->_fruitArray;
}
}
The table that I am attempting has a bunch of "fruits" in it, an example of how the table is structured:
==================================
| ID | NAME |
==================================
| 01 | Apple |
==================================
I am attempting to test this all out by using the following:
echo "<pre>";
echo "TESTING FRUIT ARRAY:</br></br>";
$basket = new FruitBasket();
echo"</br></br> PRINT_R: </br></br>";
print_r($basket->pickFruit(10));
echo "</br></br> VARDUMP: </br></br>";
var_dump($basket->pickFruit(10));
The sql query prepares and executes properly, I can do a vardump of the prepares and the binds and they return TRUE. Nothing is returned on the last query however.
In the first query that executes Doing a print statement of $allFruits shows the correct total count from the table.
The second query seems to be working properly,the string $fruitIDs, gets random id's from the table, I can echo this out and confirm that indeed the correct number of ID's are returned.
The problem occurs (I think) with the third query:
Nothing is returned form this query. The prepare statement returns true on a var dump as does the execute, however there is no results!
If I manually take the ID's that are output from query#2 and run it myself in mysql, the correct "fruit" names are returned.
Am I binding the variables incorrectly? I read the pages from the PHP manual but clearly I am doing something wrong.
Please help! :)
Thanks to the links and input provided by Your common sense, using the following:
Reference - frequently asked questions about PDO
and
Can I bind an array to an IN() condition?
I was able to resolve this by changing my query as follows:
//#2 calculate random number to pull from all of id's
$sql = "SELECT id FROM `fruits` WHERE RAND()* ? < ? ORDER BY RAND() LIMIT 0, ? ";
if ($query = $this->_db->prepare($sql)) {
$query->bindParam(1, $allFruits[0], PDO::PARAM_INT);
$query->bindParam(2, $doubleCount, PDO::PARAM_INT);
$query->bindParam(3, $count, PDO::PARAM_INT);
if ($query->execute()) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
$fruitIDs[] = $row['id'];
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #2"); }
} else {
print_r("ERROR CHECK SQL SYNTAX #2");
}
//#3 get the fruits
$inQuery = implode(',', array_fill(0, count($fruitIDs), '?'));
$sql="SELECT NAME FROM `fruits` WHERE `id` IN($inQuery)";
if ($query = $this->_db->prepare($sql)) {
if ($query->execute($fruitIDs)) {
while ($row = $query->fetch(PDO::FETCH_NUM)) {
$this->_fruitArray[] = $row[0];
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #3");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #3");
}
return $this->_fruitArray;
}
I do not fully understand the security benefits or ramifications of binding the parameters or simply including them in the actual execute() but for now the query is performing as intended, so thank you for the input!

How to pass an array of conditions to a select query class?

I am trying to build a class to select records from table defined on user input.
my plan is to make a class that read user input conditions as an array, because there is a lot of other forms for other tables with different number of conditions. therefore I want to make one class that works with all.
basically am trying to achieve this statement:
select * from table_name where condition1=value1 AND condition2=value2
where the number of conditions varies depends on user input.
what I have done is this class:
class SelectQuery {
private $Error;
public $conn;
public $table;
public function __construct($conn){ //establish connection
$this->conn = $conn;
}
public function SelectData($table,$cols,array $conds){
$this->table = $table;
if($cols)
{
if($conds)
{
$i=0; $cond='';
foreach ($conds as $key => $value){
if($i==0)
{
$cond .= $key. '=' .$value;
}
else
{
$cond .= ' AND ' .$key. '=' .$value;
}
$i++;
}
$sql = 'SELECT '.$cols.' FROM '.$table.' WHERE '.$cond.'';
$stmt = oci_parse($this->conn, $sql);
oci_execute($stmt);
$rows = oci_fetch_all($stmt, $data, null, null, OCI_FETCHSTATEMENT_BY_COLUMN);
if($rows)
{
return $rows['0']['0'];
}
else
{
return NULL;
}
}
else
{
$sql = 'SELECT {$cols} FROM {$table}';
$stmt = oci_parse($this->conn, $sql);
oci_execute($stmt);
$rows = oci_fetch_all($stmt, $data, null, null, OCI_FETCHSTATEMENT_BY_COLUMN);
if($rows)
{
return $rows['0']['0'];
}
else
{
return NULL;
}
}
}
}
and this is how the data is sent from form:
$manager = new OracleConnectManager();
$conn = $manager->establishConnection(); //establish connection
if( $conn ){
$table = 'MAIN';
$cols= '*';
$conds = array(
'id_number' => $_POST['id_number'],
'firstname' => $_POST['firstname'],
'secondname' => $_POST['secondname'],
'thirdname' => $_POST['thirdname'],
'familyname' => $_POST['familyname'],
'department' => $_POST['department'],
);
$sel = new SelectQuery($conn);
$dth = $sel->SelectData($table,$cols,$conds);
echo $dth;
}
when I run the code i get this error:
Warning: oci_execute(): ORA-00936: missing expression
(in the SelectQuery class, line: oci_execute($stmt); )
Warning: oci_fetch_all(): ORA-24374: define not done before fetch or execute and fetch
(in the SelectQuery class, line: $rows = oci_fetch_all($stmt, $data, null, null, OCI_FETCHSTATEMENT_BY_COLUMN); )
and there is no output.
any help would be appreciated.
thanks
There are two things:
You need to validate all the $_POST values and build $conds array for values that are set in $_POST.
Update:
i.e. if you have only one or two post values then the $conds array should be:
$conds = array(
'id_number' => $_POST['id_number'],
'firstname' => $_POST['firstname']
)
In your foreach loop, $value needs to be quoted if it is varchar type as:
foreach ($conds as $key => $value){
if($i==0)
{
$cond .= "$key='$value'";
}
else
{
$cond .= " AND $key='$value'";
}
$i++;
}
Update:
if you have int and varchar fields then do formatting when building array itself:
$conds = array(
'id_number' => $_POST['id_number'], // int type
'firstname' => "'".$_POST['firstname']."'" // string type
)
else
{
$sql = 'SELECT ' . $cols . ' FROM ' . $table;
// Other Statements
}
Hope the ELSE part for $sql should be framed like above?

PHP PDO Insert into database function issue

So I'm creating a basic cms application to try and learn PDO a bit more in depth, but I seem to have stumbled upon an issue. I want to be able to use a function to insert data into a MySQL database, and so far I've got this code...
public function insert($table, $rows, $values) {
$data = $values;
$STH = $this->DBH->("INSERT INTO `" . $table . "` (". $rows . ") values (?, ?, ?)");
}
Do the question marks for the values represent the number of pieces of data I will be entering into the database? If so how can I know the amount of pieces of data that are going to be entered based upon the $data array? (All $values will be entered with the format 'value, value2, value3' and so on)
Hopefully I've made myself clear on what I'm asking, I'm pretty bad at explaining myself haha.. Thanks.
You could do a prepared statement with a random number of values, as long as your values are an array. Example (untested):
public function insert($table, $rows, $values) {
$params = rtrim(", ", str_repeat("?, ", count($values)));
try {
$stmt = $this->DBH->prepare("INSERT INTO $table ($rows) VALUES ($params)");
for($i = 1; $i <= count($values); $i++) {
$stmt->bindValue($i, $values[$i-1]);
}
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch(PDOException $e) {
echo "Oops... {$e->getMessage()}";
}
}
Edit Passing only two parameters ($table and $data as an associative array), you could do something like this (untested):
public function insert($table, $data) {
$keys = array_keys($data);
$values = array_values($data);
$params = rtrim(", ", str_repeat("?, ", count($values)));
try {
$stmt = $this->DBH->prepare("INSERT INTO $table (`".implode("`,`", $keys)."`) VALUES ($params)");
$stmt->execute($values);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch(PDOException $e) {
echo "Oops... {$e->getMessage()}";
}
}
As long as your $data variable is an array, you can just pass it to the execute method in the PDO::Statement after it's been prepared. When question marks are used for binding it just requires a non-associative array of values in the order that you want them bound (in the order your question marks appear in the query)
public function insert($table, $rows, $values) {
$data = $values;
$STH = $this->DBH->prepare("INSERT INTO `" . $table . "` (". $rows . ") values (?, ?, ?)");
$STH->execute($data);
}
You can also use names for binding:
public function insert($table, $rows, $values) {
$data = array(":col1" => $values[0], ":col2" => $values[1], ":col3" => $values[2]);
$STH = $this->DBH->prepare("INSERT INTO `" . $table . "` (". $rows . ") values (:col1, :col2, :col3)");
$STH->execute($data);
}

Assigning "id's" to mysql rows for use with an associative array

I've been making a search system for my website and I would like it to display a number of results with the data pulled from the mysql database. This caused a little problem, so I looked around the web for some help and found that you can actually use mysql_fetch_array or mysql_fetch_assoc to display the results. However, when I run the search form and the action page, my localhost server freezes and my computer starts to overheat (laptop).
How would I solve this?
Btw, I did try to use while ($user = $mysql->tableCheckArray(_parameters_)) (as I found on some tutorials) but the same thing happened as stated above.
Here's my search code:
if (isset($_POST[search]) && !empty($_POST[search])){
$search = $_POST[search];
$mysql = new mysqlHandle;
$mysql->accessOpen('root', '', 'Portfolio', 'localhost');
// Sets $rows to number of entries with the 'Name' value of $search.
$rows = $mysql->tableCheck('*', stats, Name, $search, Portfolio);
echo '<div id="content">
<div id="content_main">
<h2>Search Results </h2>
<br/><hr/><br/>';
if ($rows>0){
// Sets the row's 'id' / entry number.
$num = 1;
while ($num<$rows){
$user = $mysql->tableCheckArray('*', stats, Name, $search, Portfolio);
echo $user[Name]."<br/>";
echo $user[Image]."<br/>";
echo $user[Age]."<br/>"."<br/>"."<br/>"."<hr/>"."<br/>";
$num++;
}
/*
while ($num<=$rows){
$user = $mysql->tableCheckAssoc('*', stats, Name, $search, Portfolio);
echo $user[Name][$num]."<br/>";
echo $user[Image][$num]."<br/>";
echo $user[Age][$num]."<br/>"."<br/>"."<hr/>"."<br/>";
$num++
}
*/
}else{
echo "No users found";
}
echo '</div>
</div>';
}
Here's my MySQL code:
function tableCheckAssoc($span, $table, $column, $value, $base){
$this->span=$span;
$this->table=$table;
$this->column=$column;
$this->value=$value;
$this->database=$base;
mysql_select_db($this->database);
$this->query10="SELECT $this->span FROM $this->table WHERE $this->column = '$this->value'";
$this->result10=mysql_query($this->query10) or die("<br/>"."Invalid $table CHECK query: " .mysql_error());
return mysql_fetch_assoc($this->result10);
}
// Returns array.
function tableCheckArray($span, $table, $column, $value, $base){
$this->span=$span;
$this->table=$table;
$this->column=$column;
$this->value=$value;
$this->database=$base;
mysql_select_db($this->database);
$this->query4="SELECT $this->span FROM $this->table WHERE $this->column = '$this->value'";
$this->result4=mysql_query($this->query4) or die("<br/>"."Invalid $table CHECK query: " .mysql_error());
return mysql_fetch_array($this->result4);
}
// Returns number of rows.
function tableCheck($span, $table, $column, $value, $base){
//accessOpen();
$this->span=$span;
$this->table=$table;
$this->column=$column;
$this->value=$value;
$this->database=$base;
mysql_select_db($this->database);
$this->query="SELECT $this->span FROM $this->table WHERE $this->column = '$this->value'";
$this->result=mysql_query($this->query) or die("Invalid $table CHECK query: " .mysql_error());
return mysql_num_rows($this->result);
}
I would obviously like the 'Name', 'Image' and 'Age' values of the first queried row to be displayed and then the third et.c
tableCheckArray() is returning the first matching record every time. Every time you call it, you have to re-run the query and are passing it the same result set.
Instead have tableCheck() pass back the result.
function tableCheck($span, $table, $column, $value, $base){
mysql_select_db($base);
$sql = "SELECT $span FROM $table WHERE $column = '$value'";
return mysql_query($sql) or die("Invalid $table CHECK query: " .mysql_error());
}
Then you would run something like this:
$recs = tableCheck($span, $table, $column, $value, $base);
while($row = mysql_fetch_array($recs)){
// print $row
}
The tableCheck functions are highly innefficient because they require you to keep running queries and they only ever return the first record. Also, there is no point in $this->span = $span. $span is already defined, so don't use more memory re-defining it.

How do you perform a dynamic php, PDO prepared statement Update?

I'm having trouble finding good documentation on pdo update prepared statements and even more trouble finding documentation on dynamically updating the database with pdo prepared statements. I've gotten my dynamic insert to work but am having trouble with the update. The error I'm getting is:
Warning: PDOStatement::execute() [pdostatement.execute]:
SQLSTATE[HY093]: Invalid parameter number: parameter was not defined
in
/Users/scottmcpherson/Sites/phpsites/projectx/application/models/db.php
on line 91 error
Here is the class I created minus a couple of methods that are irrelevant to this problem:
<?php
require_once("../config/main.php");
class Database{
protected static $dbFields = array('username', 'password');
public $db;
public $tableName = 'users';
public $id = 1;
public $username = "Jonny";
public $password = "Appleseed";
public function __construct() {
$this->connect();
}
public function connect(){
try {
$this->db = new PDO("mysql:host=".DB_SERVER."; dbname=".DB_NAME, DB_USER, DB_PASS);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
}
public function properties() {
$properties = array();
foreach (self::$dbFields as $field) {
if (isset($this->field) || property_exists($this, $field)) {
$properties[$field] = $this->$field;
}
}
return $properties;
}
public function propertyValues() {
$property = $this->properties();
$propertyValues = array();
foreach ($property as $key => $value) {
$propertyValues = ":" . implode(", :", array_keys($property));
}
return $propertyValues;
}
public function polishedVals(){
// The end result of this function is:
// username=:username, password=:password
$props = $this->properties();
$phaseOne = array();
foreach ($props as $key => $value) {
$phaseOne[$key] = ":".$key;
}
$phaseTwo = array();
foreach ($phaseOne as $key => $value) {
$phaseTwo[] = "{$key}={$value}";
}
$polishedVals = implode(", ", $phaseTwo);
return $polishedVals;
}
public function update(){
$stmt = "UPDATE ". $this->tableName." SET ";
$stmt .= $this->polishedVals();
$stmt .= "WHERE id=" . $this->id;
$stmt = $this->db->prepare($stmt);
if($stmt->execute($this->properties())) {
echo "yes";
} else {
echo "error ";
}
}
}
$database = new Database();
echo$database->update();
?>
With all the variables replaced with the actual values, the result I'm going for with the update() method would look like this:
public function update(){
$stmt = "UPDATE users SET ";
$stmt .= "username=:username, password=:password ";
$stmt .= "WHERE id=1";
$stmt = $this->db->prepare($stmt);
if($stmt->execute($this->properties())) {
echo "yes";
} else {
echo "error ";
}
}
In addition to spotting this problem, please let me know if you see any other issues with this code. I'm still kind of new to PHP.
Edit: I've now created a new method that adds a : to the beginning of each key in the properties array:
public function colProperties(){
$properties = $this->properties();
$withCols = array();
foreach($properties as $key => $value){
$withCols[":".$key] = $value;
}
return $withCols;
}
So my update() method now looks like:
public function update(){
$stmt = "UPDATE ". $this->tableName." SET ";
$stmt .= $this->polishedVals();
$stmt .= "WHERE id=" . $this->id;
$stmt = $this->db->prepare($stmt);
if($stmt->execute($this->colProperties())) {
echo "yes";
} else {
echo "error ";
}
}
and if I var_dump($this->colProperties) I get:
array(2) { [":username"]=> string(5) "Jonny" [":password"]=> string(9) "Appleseed" }
And still getting the same error.
I don't think that passing parameters to an UPDATE query requires a different method than a SELECT one. The information in the PDOStatement->execute() manual page should apply:
<?php
/* Execute a prepared statement by passing an array of insert values */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour');
$sth->execute(array(':calories' => $calories, ':colour' => $colour));
?>
You are using named parameters so execute() expects an associative array. Use var_dump() to display $this->properties() right before execute():
var_dump($this->properties())
Make sure you keys match exactly.
The error is that in between
$stmt .= $this->polishedVals();
$stmt .= "WHERE id=" . $this->id;
There needs to be a space in between the WHERE clause as the polishedVals() method does not add a space after the implode. So, you'll have something like
UPDATE User SET city=:city, location=:locationWHERE User.id=28
Which causes the error.
Simple bug.

Categories