I've been studying PHP for 2 months now as my first scripting language. For most of my problems i can easily find an answer online, but there's something about PDO that i can't seem to understand.
In order to retrieve data from a database I instantiate a new object of the PDO class and call the PDO::query() method on it. This returns a PDOStatement object which carries the result set from the SQL query. Here's where the problem starts. I can't seem to understand how and where the data from the result set is stored.
In the PHP Manual i learned to display the returned rows by iterating over the PDOStatement object with a foreach loop. However, the PHP manual clearly states that if an object is converted to an array, the result is an array whose elements are the object's properties. The PDOStatement only has one property - $queryString - containing the issued query string. So... where are the query results stored? And why can I reach them through an array with a foreach loop, but not outside of it?
// Instantiate new PDO object to establish a new connection with MySQL database
$db = new PDO('mysql:dbhost=localhost;dbname=world', 'root', 'secret');
// Execute SQL query - Returns a PDOStatement object
$result = $db->query("SELECT Name, Continent, Population FROM Country");
// Result set can be accessed with a foreach loop iterating over the PDOStatement object
foreach ($result as $row) {
echo "$row[Name] - $row[Continent] - $row[Population] <br />";
}
// Outside the foreach loop, $result cannot be accessed this way.
// This produces 'Cannot use object of type PDOStatement as array'
echo $result[0]['Name'];
The PDOStatement class implements the Iterator interface, which lets its objects be iterated through.
Iterator extends Traversable {
/* Methods */
abstract public mixed current ( void )
abstract public scalar key ( void )
abstract public void next ( void )
abstract public void rewind ( void )
abstract public boolean valid ( void )
}
For an object that implements the Iterator interface,
foreach($result as $row) {
// Code
}
is equivalent to
for ($result->rewind(); $result->valid(); $result->next()) {
$row = $result->current();
// Code
}
Related
I am currently in the process of updating my web application from PHP 5.6 to PHP 7, therefore I need to alter the database abstraction class that has been using the old MySQL-Extension.
The class has a method "Query()" where SQL statements are being prepared and executed and a method "Fetch()" which fetches a single row and returns it. See the simplified versions:
function Query($query='')
{
$result = $this->connection->Prepare($query);
$result->Execute();
return $result;
}
function Fetch($resource=null)
{
return $resource->Fetch(PDO::FETCH_ASSOC);
}
So basically what I am trying to do is this:
$statement = Query('SELECT * FROM Test');
$firstRow = Fetch($statement);
print_r($statement) shows me this:
PDOStatement Object
(
[queryString] => SELECT * FROM Test
)
But when I call "Fetch", there is no row returned. Can someone tell if it's possible to return PDOStatement objects and pass them into other functions/methods for "later use"?
PS: Of course I know that this is not best practice but I am trying to avoid having to alter thousands of lines of code in the application.
I am working small php project and i am using singleton database class from this repo
The problem is i can't get query result as array value.Here is my code
Core.php
class Core extends Database
{
public static function run($sql)
{
return parent::getInstance()->getConnection()->query($sql);
}
public function getUniversities()
{
$sql = 'SELECT * FROM `adm_universities`';
return Core::run($sql);
}
}
//get the data from db
$result=Core::getUniversities();
print_r($result);
But the PDOStatement Object returns only the queryString not the result array.
what's wrong with this code?
You can either use foreach loop over PDOStatement or get a conventional array from it using fetchAll() method.
foreach ($result as $row) ...
// or
$data = $result->fetchAll();
print_r($data);
Note that this singleton class makes very little sense as it doesn't support prepared statements.
Also, there should be no getUniversities() in the Core class. That's bizarre thing for the OOP.
You have not used fetch or fetchall or find it seems.
Check the pdo manual properly.
You have to execute the query and then you have to use result functions to get the result array
When you implement the _toString method on a class, you are able to convert the object in string
$string =(string) $object
Is there an equivalent for converting in array
$array=(array) $object
From what I have tested, with this code, the attributes of the objet are transformed in index of the array, even if this object implement ArrayAccess.
I expected that casting an object with array access, I would obtain an array thith the same values I could access with the object
public class MyObject implements ArrayAccess{
private $values;
public function __construct(array $values){
$this->values=$values;
}
public function offsetSet($name,$value){
$this->values[$name]=$value;
}
//etc...
}
$myObject=new MyObject(array('foo'=>'bar');
$asArray=(array)$myObject;
print_r($asArray);
// expect array('foo'=>'bar')
// but get array('MyObjectvalues'=>array('foo'=>'bar'));
I also Notice that the native ArrayObject class has a the behavior I expected
No, there is no magic function to cast object as array.
ArrayObject is implemented with C and has weird specific behaviors.
Implement custom method asArray and use it.
Actually, it's impossible to write a general function:
/*
* #return array ArrayAccess object converted into an array
*/
function (ArrayAccess $arrayAccessObject): array { /* ... */ }
Why? Because ArrayAccess interface just gives a way to use $aa[/*argument*/] syntax, but does not give a way to iterate over all possible arguments.
We used to think that array has a finite number of keys. However ArrayAccess let us create objects having an infinite set of keys (note, the same concerns Traversable: i.e. prime numbers are "traversable").
For example, one can write a class, implementing ArrayAccess, that acts like a HTTP client with a cache (I'm not saying that it's a good idea; it's just an example). Then offsetExists($url) tells if a URL gives 200 or not, offsetGet($url) returns a content of a URL, offsetUnset($url) clears cached content, offsetSet throws a LogicException, 'cause setting a value makes no sense in this context.
// ...
if (empty($client['https://example.com/file.csv'])) {
throw new RuntimeException('Cannot download the file');
}
$content = $client['https://example.com/file.csv'];
// ...
Or maybe one wants to read/write/unset (delete) files with ArrayAccess.
Or maybe something like (set of even numbers is infinite):
$even = new EvenNumberChecker(); // EvenNumberChecker implements ArrayAccess
$even[2]; // true
$even[3]; // false
$even[5.6]; // throws UnexpectedValueException
isset($even[7.8]); // false
$even[0] = $value; // throws LogicException
ArrayAccess objects from academic examples above cannot be converted into finite arrays.
You can use json_decode and json_encode to get the most generic function for it:
public static function toArray(ArrayAccess $array): array
{
return json_decode(
json_encode($array),
true
);
}
What I'm doing
I'm attempting to get an array of Image objects using the following:
(If I run the raw MySQL query, I am returned 3 distinct rows, all with different values, except for the product_id, obviously.)
$query = 'SELECT * FROM `j_images` WHERE product_id = :product_id';
$stmt = $db_conn->prepare($query);
if($stmt)
{
$I = new \jenis\Product\Image();
$stmt->setFetchMode(PDO::FETCH_INTO, $I);
$result = $stmt->execute(array('product_id'=>$product_id));
if($result)
{
$images = $stmt->fetchAll();
var_dump($images);
}
}
What I Get
An array with 3 jenis\Product\Image objects, but the objects (including references) are identical.
What I expect
An array with 3 \jenis\Product\Image objects, each unique.
Is this because it is fetching into the same object (i.e. $I)? If so, is there a way around this?
However Example 4 in the PHP documentation would lead me to believe that this is possible.
If I follow the example directly from the documentation:
$images = $stmt->fetchAll(PDO::FETCH_CLASS, "\jenis\Product\Image");, I get three separate objects, but all properties are NULL.
Additional Information
Here is a stripped down version of my Image Class:
namespace jenis\Product;
use jenis\DB as DB;
use \PDO as PDO;
class Image
{
public $id;
public $product_id;
public $url;
public static function getImagesByProduct($product_id)
{
… code outlined above …
}
}
The code outlined above is executed as a static method (e.g. Image::getImagesByProduct($product_id);
The reason this wasn't working was because I had developed my constructor so that optional parameters could be passed.
For example:
function __construct($name='', $description='')
{
$this->name = $name;
$this->description=$description;
}
Because the constructor is utilized when FETCH_CLASS is called, this was causing my variables to be NULL, as the properties are not passed as parameters. As #Digital Chris noted, I needed PDO::FETCH_PROPS_LATE which allowed the properties to be set after the constructor was called.
Codeigniter can return a database query as generic "Object" like:
$q = $this->db->get("some_table");
$obj = $this->q->row();
$var = $obj->some_property
In my case I want to make a PHP class who's public variables are 1 for 1 with the database columns, along with some public methods. Is there a quick one-shot way to cast or convert the generic "Row" object into my custom class object? I've read posts that hint that it is certainly possible, but most involve a really hacky serialize/deserialize solution. In the past I have just done:
public function __construct($row) {
$this->prop = $row->prop;
$this->id = $row->id;
$this->value = $row->value;
}
And I find this is very tedious and makes ugly code.
See the third section under result():
CodeIgniter User Guide: Generating Query Results
You can also pass a string to result() which represents a class to instantiate for each result object (note: this class must be loaded)
$query = $this->db->query("SELECT * FROM users;");
foreach ($query->result('User') as $row)
{
echo $row->name; // call attributes
echo $row->reverse_name(); // or methods defined on the 'User' class
}