I am trying to fetch query results so i can use the data from mysql as variables on my page
here in the given code i have hardcoded the hotel value as 105 but i am trying to get the value from an other page as get .
$ratedID =$_GET['ratedhotel'];
Here the body of my page is embedded under while.but the page is always empty.and this is what i have at the moment.
$hotel=$database->query('SELECT * FROM rate WHERE HotelID=:hotel ');
$database->bind(':hotel', '105');
$hotel->setFetchMode(PDO::FETCH_ASSOC);
while($r = $hotel->fetch())
Below Is what i had before.
//$query = $db->query("SELECT * from rate WHERE HotelId='$ratedhotelID'");
//while($hotel = mysql_fetch_row($query, MYSQL_ASSOC))
I have some defined functions which I got as a library to use.
<?php
class Database{
private $host=DB_HOST;
private $user=DB_USER;
private $pass=DB_PASS;
private $dbname=DB_NAME;
private $stmt;
private $dbh;
private $error;
public function __construct()
{
// Set DSN
$dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
// Set options
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
// Create a new PDO instanace
$this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
}
public function query($query){
$this->stmt = $this->dbh->prepare($query);
}
public function bind($param, $value, $type = null){
if (is_null($type)) {
switch (true) {
case is_int($value):
$type = PDO::PARAM_INT;
break;
case is_bool($value):
$type = PDO::PARAM_BOOL;
break;
case is_null($value):
$type = PDO::PARAM_NULL;
break;
default:
$type = PDO::PARAM_STR;
}
}
$this->stmt->bindValue($param, $value, $type);
}
public function execute(){
return $this->stmt->execute();
}
public function resultset(){
$this->execute();
return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function single(){
$this->execute();
return $this->stmt->fetch(PDO::FETCH_ASSOC);
}
public function rowCount(){
return $this->stmt->rowCount();
}
}
I am not sure and am new to PDO .Any help will be much appreciated.
Jai
UPDATE
I made Changes to php and the code is like below:
$database= new Database();
$database->query('SELECT * from rated where HotelID =:hotel');
$database->bind(':hotel', $HotelID);
//echo $database->resultset();
$rows=array();
$rows= $database->resultset(PDO::FETCH_ASSOC);
//while($rows= $database->resultset());
//while($hotel = $q->fetchAll(PDO::FETCH_ASSOC));
{
echo "<pre>";
print_r($rows);
echo "</pre>";
echo "<div>'.$rows[LowRate].'<div>";
};
?>
So based on the above i can see the array on my page so the query works.
But I want to use the variables in my html like.
<p class="price-display-large page-price-text">'.$rows[LowRate].''.rows[PropertyCurrency].'</p>
But this does not work.
Just replace the harcoded value '105' with a reference to $ratedId.
$database->bind(':hotel', $ratedId);
I don't understand why that wouldn't work for you. Include the bind type, if you want something other than what the library function is going to assign.
$database->bind(':hotel', $ratedId, PDO::PARAM_INT);
(It's likely I entirely misunderstood the question you were asking.)
FOLLOWUP
I expect rows is a multidimensional array, since it looks like a "fetch all" operation has been done. Each row will be an array, but those are all going to be contained in another array.
To see what's in the array, you could do a:
var_dump($rows);
To get the LowRate value from the first row, I think you'd need to refer to it like this:
$rows[0]['Lowrate']
(I think it's zero based, if not, it will be a [1]. The var_dump will show you what it looks like.)
Just because you got an array back, that doesn't mean there's rows in it, it could be an empty set. An resultset with zero rows is a valid resultset, so you really want to check if the row you want is in there or not.
The normative pattern would be to loop through the array of rows, and handle each row individually. For example, using a foreach loop.
foreach($rows as $row) {
echo $row['LowRate'];
}
Related
I'm wanting to create a new instance of my Class and assign it's attributes the values that are returned. The reason for this is I'm creating a series of methods inheriting from the calling class, as opposed to using static methods which I already had working.
Example of what I'm using currently:
public static function findById($id) {
$id = self::escapeParam($id);
$idVal = is_int($id) ? "i" : "s";
$sql = "SELECT * FROM ".static::$db_table." WHERE id = ? LIMIT 1";
return static::findByQuery($sql,$idVal,$id);
}
public static function findByQuery($sql,$bindChar = '',$bindVal = '') {
try {
$callingClass = get_called_class();
$object = new $callingClass;
$statement = Database::$connection->prepare($sql);
if(!empty($bindChar)) :
$statement->bind_param($bindChar, $bindVal);
endif;
if($statement->execute()) :
$result = $statement->get_result();
$object = $result->fetch_object();
endif;
$statement->close();
if(!empty($object)) :
return $object;
endif;
} catch(Exception $e) {
}
}
What I tried was writing an instantiation method that creates a new instance of my class, and then assign each attribute of the object the value it returns from an array from a tutorial I did. However, the tutorial was fairly outdated and didn't use any new syntax or binding, so I was trying to rework this.
Example from the tutorial below:
public static function find_by_id($id) {
global $database;
$the_result_array = static::find_by_query("SELECT * FROM " . static::$db_table . " WHERE id = $id LIMIT 1");
return !empty($the_result_array) ? array_shift($the_result_array) : false;
}
public static function find_by_query($sql) {
global $database;
$result_set = $database->query($sql);
$the_object_array = array();
while($row = mysqli_fetch_array($result_set)) {
$the_object_array[] = static::instantation($row);
}
return $the_object_array;
}
public static function instantation($the_record){
$calling_class = get_called_class();
$the_object = new $calling_class;
foreach ($the_record as $the_attribute => $value) {
if($the_object->has_the_attribute($the_attribute)) {
$the_object->$the_attribute = $value;
}
}
return $the_object;
}
private function has_the_attribute($the_attribute) {
return property_exists($this, $the_attribute);
}
What I was trying to do from the tutorial, was to return my result as an array using a while, and then assigning a variable by passing the built array into the static::instantation() method, but it doesn't seem to ever be working correctly, as any public functions I create in my calling class (Admin for example) aren't called after as they don't exist due to the Class not being instantiated.
mysqli_result::fetch_object() accepts the class name as the first argument. You can pass the class name as an argument to that method and get the instance of the model. I am not sure why you have that much code but consider my example which I wrote based on your own code:
<?php
class Model
{
public static function findByQuery(string $sql, ?string $bindChar = null, ?string $bindVal = null): ?static
{
$statement = Database::$connection->prepare($sql);
if ($bindChar) :
$statement->bind_param($bindChar, $bindVal);
endif;
$statement->execute();
$result = $statement->get_result();
return $result->fetch_object(static::class);
}
}
class User extends Model
{
private $id;
}
class Database
{
public static mysqli $connection;
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
Database::$connection = new mysqli('localhost', 'user', 'password', 'test');
$user = User::findByQuery('SELECT ? as id', 's', 'Dharman');
var_dump($user);
The output from that example is:
object(User)#4 (1) {
["id":"User":private]=>
string(7) "Dharman"
}
As you can see, the code created an instance of the class using late-static binding and it also assigned the value to a private property, which you can't do otherwise.
P.S. My example is a little bit tidier. I added parameter typing and removed a lot of unnecessary code. In particular, I remove empty try-catch which is a terrible practice.
I have now got this working, although I feel this is probably not the best way of doing it.
I'm primarily front end so please comment if there are improvements or best practices.
public static function findByQuery($sql,$bindChar = '',$bindVal = '') {
try {
$statement = Database::$connection->prepare($sql);
if(!empty($bindChar)) :
$statement->bind_param("$bindChar", $bindVal);
endif;
if($statement->execute()) :
$result = $statement->get_result();
$output = $result->fetch_object();
endif;
$statement->close();
if(!empty($output)) :
$class = get_called_class();
$object = new $class;
foreach(get_object_vars($output) as $key => $value) :
$object->$key = $value;
endforeach;
endif;
if(!empty($object)) :
return $object;
endif;
} catch(Exception $e) {
}
}
My initial thoughts were declaring an object and then I thought that the PHP fetch_object call would have just assigned my object it's properties after initiating the Class but that wasn't the case.
So what I've done is that if the statement is successful and a results object is created, I then get the object properties and values with the get_object_vars() command, and then loop through these as a key value pair, assigning each attribute it's returned value.
I can confirm this works as I can now run $admin->remove() from my removal script, as opposed to what I was having to do before which was Admin::remove($id);
My MySQL randomly assigns a second port to the requested address like this:
[2002] Cannot assign requested address (trying to connect via tcp://127.0.0.1:3306:3306)
This behavior is triggered on my localhost and on my servers as well, so I figured it might be my code. I'm connecting via a self-written class, which is using constants only to connect (which are correct, no second ports assigned within these constants), so I'm pretty clueless why this behavior is triggered sometimes and where the second port comes from. The execution of the script terminates when this error occurs.
I added the class to this post, if someone wants to browse through.
Any advise is welcome to fix or circumnavigate this.
Thanks in advance!
class mysql{
protected $query = false;
protected $lastquery = false;
protected $result = false;
protected $row = false;
protected $args = array('');
protected static $mysqli = null;
public function __construct(\mysqli $mysqli = null){
self::$mysqli = $mysqli;
$this->establishAutoConnection();
}
// Tries to establish connection, if none is set.
protected function establishAutoConnection(){
IF(empty(self::$mysqli)):
SWITCH($_SERVER['SERVER_NAME']):
case 'localhost':
self::$mysqli = new mysqli(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWD, MYSQL_DATABASE);
break;
case 'AN IP.IP.IP':
$this->connectToSlave();
break;
default:
self::$mysqli = new mysqli(MYSQL_HOST_SERVER, MYSQL_USER_SERVER, MYSQL_PASSWD_SERVER, MYSQL_DATABASE_SERVER);
ENDSWITCH;
ENDIF;
}
public function connectToSlave(){
self::$mysqli = new mysqli(SLAVE_HOST_SERVER, SLAVE_USER_SERVER, SLAVE_PASSWD_SERVER, SLAVE_DATABASE_SERVER);
}
public function connectToMaster(){
self::$mysqli = new mysqli(MASTER_HOST_SERVER, MASTER_USER_SERVER, MASTER_PASSWD_SERVER, MASTER_DATABASE_SERVER);
}
// Sets the PDO arguments, which need to be replaced by '?'
public function setArgs(&$data, $type = false){
$type = $type ?: $this->getTypeString($data);
$this->args[0] .= $type;
$this->args[] = &$data;
return $this;
}
// Reset function needs to be called in order to make a new query.
public function reset(){
$this->args = array('');
$this->row = false;
$this->result = false;
return $this;
}
// Loops through the found results.
public function loopThroughResults(){
return ($this->row = $this->result->fetch_assoc())
? true
: false;
}
// Returns the row unformatted. If no result is found an emtpy array is returned.
public function getRow(){
$this->row = $this->row ?: $this->result->fetch_assoc();
return $this->row ?: array();
}
// Returns the first result of the first row.
public function getSingleResult(){
FOREACH($this->getRow() as $assoc => $value):
return $value ?: false;
ENDFOREACH;
return false;
}
// Returns the result by rowname
public function getByName($name){
return isset($this->row[$name])
? $this->row[$name]
: false;
}
// If a new query is made, while the former query has not been resetted, a warning is stored or an error is thrown.
protected function isResetted(){
IF($this->result):
$this->warning("PDO has not been resetted from query: ". $this->lastquery ." // new query: ". $this->query);
ENDIF;
}
// Executes the prepared query.
public function query($sql){
$this->query = $sql;
$this->isResetted();
$this->lastquery = $sql;
IF($prep = self::$mysqli->prepare($this->query)):
IF(count($this->args) > 1):
call_user_func_array(array($prep, 'bind_param'), $this->args);
ENDIF;
$prep->execute();
$this->result = $prep->get_result();
$prep->close();
ELSE:
$this->error("Query $sql failed to prepare.");
ENDIF;
}
// Automatically generates the string of types for the submitted params if not set. ("ssisdss") etc.
protected function getTypeString($string){
SWITCH(gettype($string)):
case 'string':
return 's';
case 'double':
return 'd';
case 'boolean':
case 'integer':
return 'i';
case 'array':
$this->error('Unserialized array submitted to PDO.');
break;
default:
$this->error('Unknown param type submitted to PDO. ' . print_r($string) . ' type: ' . gettype($string));
break;
ENDSWITCH;
}
protected function error($msg){
IF(!new error($msg)):
trigger_error($msg);
ENDIF;
}
protected function warning($msg){
IF(!new warning($msg)):
trigger_error($msg);
ENDIF;
}
}
Did you added the port number in your definition? So e.g.
MYSQL_HOST = 127.0.0.1:3306
MYSQL_HOST_SERVER = 127.0.0.1:3306
mysqli will use his default port settings on your server definition. So if you add the port number here, the result will be the same as your request error:
MYSQL_HOST = 127.0.0.1:3306:3306
MYSQL_HOST_SERVER = 127.0.0.1:3306:3306
I am using PHP with OOP to select rows from the database (MySQL).
When I execute the query, it returns an empty row.
Here is the classe I am using:
<?php
class EmploiManager
{
private $_db;
public function __construct($db)
{
$this->setDb($db);
}
public function category($category)
{
$q = $this->_db->prepare('SELECT * FROM DemandeEmploi WHERE category = :category');
$q->execute(array('category' =>$category));
$donnees = $q->fetch(PDO::FETCH_ASSOC);
return new Emploi($donnees);
}
public function setDb(PDO $db)
{
$this->_db = $db;
}
}
$type = $_GET['category'];
$manager = new EmploiManager($db);
$row = $manager->category($type);
foreach ($row as $demandeE)
{
?>
<div class="list"><h4><? echo $demandeE->title();?></h4> </div>
<?php
}
?>
Can any one tell me what's wrong with that code?
Thanks!
It's my bad, I didn't use a loop to select all the rows.
I corrected the code and it works fine now, here is what it looks like:
public function category($category)
{
$datas = array();
$q = $this->_db->prepare('SELECT * FROM DemandeEmploi WHERE category = :category');
$q->execute(array('category' =>$category));
while ($donnees = $q->fetch(PDO::FETCH_ASSOC))
{
$datas[] = new Emploi($donnees);
}
return $datas;
}
$q->fetch() just returns one row of the results. If you want all the results, you must use $q->fetchAll().
Since you specified PDO::FETCH_ASSOC, the elements of $row will be associative arrays, not objects; aren't you getting errors saying that you're trying to call a method on a non-object? So $demandeE->id() should be $demandeE['id'], and $demandeE->title() should be $demandeE['title'].
Alternatively, you could specify PDO::FETCH_OBJ. Then, the values will be properties, not methods, so it should be $demandeE->id and $demandeE->title (no parentheses).
So this is what I would like to achive.
I would like to use PDO, joust as it is I like it, but I would like to add some new methods on top of PDO.
Like: update(), insert(), delete(), fetchAllAssoc(), etc...
So I would have to work with both PDO and PDO Statement objects from inside one class.
Beacuse to achive update, i would have to build a query, prepare() it, that would return PDO Statement object and then I would have to bindValues and then execute.
This is what I have done so far:
<?php
namespace Database\DBAL;
use PDO;
class Database extends PDO
{
protected static $instance;
public static function getInstance()
{
if(empty(static::$instance))
{
// This will be fetched from config
$dns = "mysql:host=localhost;dbname=db_name";
$username = "root";
$password = "my_pwd";
static::$instance = new Database($dns, $username, $password);
}
return static::$instance;
}
/**
* Detect param type
*/
protected function detectType($value)
{
if(is_string($value))
{
return PDO::PARAM_STR;
}
else if(is_int($value))
{
return PDO::PARAM_INT;
}
else if(is_null($value))
{
return PDO::PARAM_NULL;
}
else if(is_bool($value))
{
return PDO::PARAM_BOOL;
}
else
{
return false;
}
}
/**
* Updates table
*/
public function update($table, $data, $identifier)
{
$set = array();
foreach ($data as $columnName => $columnValue) {
$set[] = $columnName . ' = ?';
}
$query = 'UPDATE ' . $table . ' SET ' . implode(', ', $set) . ' WHERE ' . implode(' = ? AND ', array_keys($identifier)) . ' = ?';
$params = array_merge(array_values($data), array_values($identifier));
array_unshift($params, null);
unset($params[0]);
echo $query;
$pdos = static::$instance->prepare($query);
foreach($params as $key => $param)
{
$pdos->bindValue($key, $param, $this->detectType($param));
}
return $pdos->execute();
}
}
?>
You would use it like this:
use Database\DBAL\Database;
$db = Database::getInstance();
$db->update("users", array("user_password" => "new password"), array("user_id" => 1));
My question is, is this the right way to do it?
I was reading about Decorator and Facade pattern and it dosent look like this.
What is the best way to extend PDO class with my methods that would do simple deletes, inserts and fetch some data? And I want PDO to be intact, so I can use it the way I would usualy do... What pattern should I use and what would be the best way?
Thanks!
If i'm correct you are implementing the active record pattern which is an anti pattern. Please look after for a few ORM framework.
I've created my own: DataContext class which contains all the PDO stuffess (i'm not extending the PDO!), TableEntity classes which handles all the CRUD operations and Entity classes which are "the rows from tables".
$dc = new TestDataContext(...);
$dc->users()->Where(column, 1, '=')->...
$dc->users()->InsertOnSubmit(new User())
$dc->SubmitChanges();
Like Linq to SQL. :-)
This question already has answers here:
PDO fetchAll group key-value pairs into assoc array
(2 answers)
Closed 2 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
I am using PDOStatement to query the database. Whenever I get a returned row, I want it to be fetched into an array, with the $row[0] as the key, and the subsequent elements in the row as the values.
I can, of course, write a combination of foreach loops and if conditionals to do the job, such as the below:
private static function GetMySQLResult($dbname, $sqlString) {
$dbh = self::ConstructPDOObject($dbname);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$result=array();
foreach ($dbh->query($sqlString) as $row) {
// the simplest case for 2 columns,
// should add more to handle more columns
$result[$row[0]][]=$row[1];
}
return $result;
}
but I am looking for an existing method; is there such a method already exist?
Why reopened the question.
What is asked here is clearly the combination of PDO::FETCH_GROUP|PDO::FETCH_ASSOC. PDO::FETCH_KEY_PAIR only works with a 2 column result. But here the example is a 2 column result only to simplify the code in the question, not for all situations.
Credits go to devdRew. Check the other question here.
Apparently, you can as stated in the answer. I checked as well and it works great.
$q = $db->query("SELECT `name` AS name, `value` AS value FROM `settings`;");
$r = $q->fetchAll(PDO::FETCH_KEY_PAIR);
EDIT
This answer requires that you specify maximum 2 columns: 1 key and 1 value. If you need to retrieve more keys from the database, check the answer below and read #nullabilty's comment. For those who are lazy, here is his method:
$q->fetchAll(PDO::FETCH_UNIQUE);
$stmt->fetchAll(PDO::FETCH_UNIQUE);
albeit a bit of a hack but really the only way to do this since someone decided FETCH_KEY_PAIR should require 2 column result set's only.
Note: first column is used a key and is not returned in the result set in any other form.
Afaik, there are no existing method that would do that in PHP but there are a couple of ways you could achieve what you want.
One of the first being what Xeoncross said but a bit modified :
$pdostmt = $pdo->query("SELECT ... FROM your_table");
$res = array();
foreach ($pdostmt->fetchAll(PDO::FETCH_ASSOC) as $row)
{
$res[array_shift($row)] = $row;
}
Otherwise, you can create a class with a __set() method to catch variable assignment:
class at_res
{
public $key;
public $values = array();
protected $flag = true;
public function __set($var, $value)
{
if ($this->flag)
{
$this->key = $value;
$this->flag = false;
}
else
{
$this->values[$var] = $value;
}
}
public function extract()
{
return array($this->key => $this->values);
}
}
$pdo = new PDO(...);
$pdostmt = $pdo->query("SELECT ... FROM your_table");
$res = $pdostmt->fetchObject('at_res');
var_dump($res->extract());
Hope it helps.
Some tips, you need to pass the right fetch style to the PDOStatement->fetch() method so that you don't end up with double data (numeric and textual column names). Like $row[0] and $row['id'] which both contain the same value when you use PDO::FETCH_BOTH.
$result = $dbh->query($sqlString);
while ($row = $result->fetch(PDO::FETCH_NUM)) {
...
As for your question, you will have to fetch all the results and then create an array with the $row['id'] as the key and the result row as the value - just like you are doing. I built an entire ORM library around PDO and I could never find anything to do this automatically.
$result = $dbh->query($sqlString);
$results = array();
while ($row = $result->fetch(PDO::FETCH_NUM)) {
$results[$row[0]] = $row;
}
return $results;
Besides the two column table scenario, there's nothing at the PDO level to handle this, but you could write a reusable iterator for it, like this:
class FirstColumnKeyIterator implements Iterator
{
private $stmt;
private $key;
private $data;
private $mode;
public function __construct(PDOStatement $stmt, $fetch_mode = PDO::FETCH_NUM)
{
$this->stmt = $stmt;
$this->mode = $fetch_mode;
$this->fetch();
}
private function fetch()
{
if (false !== ($this->data = $this->stmt->fetch($this->mode))) {
$this->key = current(array_splice($this->data, 0, 1));
}
}
public function rewind()
{
// nil operation
}
public function key()
{
return $this->key;
}
public function current()
{
return $this->data;
}
public function next()
{
$this->fetch();
}
public function valid()
{
return false !== $this->data;
}
}
The constructor takes a PDOStatement and an optional fetch mode (numeric columns by default, but can be changed to PDO::FETCH_ASSOC for an associative array) and lets you iterate over the query results using a typical foreach.
Usage example:
$dbh = new PDO(/* etc */);
$stmt = $dbh->query('SELECT * FROM accounts');
foreach (new FirstColumnKeyIterator($stmt, PDO::FETCH_ASSOC) as $key => $value) {
echo $key, ' = ', print_r($value, true), PHP_EOL;
}