PHP PDO: Fetching data as objects - properties assigned BEFORE __construct is called. Is this correct?

Asked
Active3 hr before
Viewed126 times

9 Answers

fetching
90%

There's a mostly undocumented PDO constant called PDO::FETCH_PROPS_LATE which you can use to cause the properties to be fetched into the object after its been constructed. For example:,That being said, yeah it is rather counter-intuitive to have FETCH_CLASS populate before calling __construct. The answer given on the mailing list is the standard copy-and-paste "RTM" answer. If FETCH_INTO works, you should open a documentation bug with the suggested enhancement(s).,PDO::FETCH_INTO: updates an existing instance of the requested class, mapping the columns of the result set to named properties in the class, Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers

There's a mostly undocumented PDO constant called PDO::FETCH_PROPS_LATE which you can use to cause the properties to be fetched into the object after its been constructed. For example:

$obj = $STH - > fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'person');
88%

The full question should be "Is this correct or is it some bug I can't count on?",Is this some bug (in which case I can't/won't count on it) or is this The Way It Should Be. Because it's really quite a nice thing the way it works currently.,I've been working with PDO more and in particular playing with fetching data directly into objects. While doing so I discovered this:,For this particular case, the string doesn't start with a number, so it becomes equal to 0 when cast.

If I fetch data directly into an object like this:

$STH = $DBH - > prepare('SELECT first_name, address from people WHERE 1');
$obj = $STH - > fetchAll(PDO::FETCH_CLASS, 'person');

and have an object like this:

class person {
   public $first_name;
   public $address;

   function __construct() {
      $this - > first_name = $this - > first_name.
      " is the name";
   }
}
load more v
72%

If I fetch data directly into an object like this:,It shows me that the properties are being assigned before the __construct is being called -- because the names all have " is the name" appended. ,I've been working with PDO more and in particular playing with fetching data directly into objects. While doing so I discovered this:,There's a mostly undocumented PDO constant called PDO::FETCH_PROPS_LATE which you can use to cause the properties to be fetched into the object after its been constructed. For example:

If I fetch data directly into an object like this:

$STH = $DBH - > prepare('SELECT first_name, address from people WHERE 1');
$obj = $STH - > fetchAll(PDO::FETCH_CLASS, 'person');
load more v
65%

When an object is fetched, its properties are assigned from respective column values, and afterwards its constructor is invoked. , Returns an instance of the required class with property names that correspond to the column names or false on failure. , Fetches the next row and returns it as an object. This function is an alternative to PDOStatement::fetch() with PDO::FETCH_CLASS or PDO::FETCH_OBJ style. ,PDOStatement::fetchObject — Fetches the next row and returns it as an object

load more v
75%

Setting class properties after calling a constructor,As you can see, a value from database has been overwritten, because by default PDO assigns class properties before calling a constructor. Which could be a problem, that, however, can be easily solved:,Assigning class properties,Fetching an array of objects

To create a single object from the query results you have two options. You can use the either a familiar fetch() method:

class User {};
$stmt = $pdo - > query('SELECT name FROM users LIMIT 1');
$stmt - > setFetchMode(PDO::FETCH_CLASS, 'User');
$user = $stmt - > fetch();

or a dedicated fetchObject() method:

class User {};
$user = $pdo - > query('SELECT name FROM users LIMIT 1') - > fetchObject('User');

Although both code snippets will give you the same instance of a User class,

/* object(User)#3 (1) {  ["name"] => string(4) "John"} */

Of course, both methods described above could be used with a familiar while statement to get consequent rows from database. Nonetheless, a handy fetchAll() method can be used to get all the returned records in the array of objects at once:

class User {};
$users = $pdo - > query('SELECT name FROM users') - > fetchAll(PDO::FETCH_CLASS, 'User');

will give you an array consists of objects of a User class, with properties filled from returned data:

/* array(2) {  [0]=> object(User)#3 (1) {    ["name"] => string(4) "John"  }  [1]=> object(User)#4 (1) {    ["name"]=> string(4) "Mike"  }} */

Note that you can combine this mode with PDO::FETCH_UNIQUE and PDO::FETCH_GROUP, to get the resulting array indexed by an unique field or to make results grouped by a non-unique field respectively. For example, the following code will return an array, where a record id will be used as array index instead of consecutive numbers.

class User {};
$stmt = $pdo - > query('SELECT id, id, name, car FROM users');
$users = - > fetchAll(PDO::FETCH_CLASS | PDO::FETCH_UNIQUE, 'User');

For example, this code

class User {
   public $name;
}
$user = $pdo - > query('SELECT * FROM users LIMIT 1') - > fetchObject('User');

will give you an object with all the properties automatically assigned, no matter, whether they exist in the class or not:

/* object(User)#3 (4) {  ["id"]   => string(3) "104"  ["name"] => string(4) "John"  ["sex"]  => string(4) "male"  ["car"]  => string(6) "Toyota"} */

From this you can tell that to avoid an automated property creation you could to use the magic __set() method to filter the properties out. The simplest filtering technique would be just an empty __set() method. With it, only existing properties will be set:

class User {
   private $name;
   public
   function __set($name, $value) {}
}
$user = $pdo - > query('SELECT * FROM users LIMIT 1') - > fetchObject('User'); /*array(1) {  [0]=> object(User)#3 (1) {    ["name":"User":private]=> string(4) "John"  }} */

Unfortunately, there is NO fetch mode for the use case when your class is accepting values in the constructor. It's impossible to have such a class to be instantiated using PDO. You'll have to resort to an explicit loop like this:

class User {
   protected $name;
   protected $car;
   public
   function __construct($name, $car) {
      $this - > name = $name;
      $this - > car = $car;
   }
}
$users = $pdo - > query('SELECT name, car FROM users') - > fetchAll(PDO::FETCH_ROW);
foreach($users as $i => $row) {
   $users[$i] = new User(...$row);
}

Let's say we have a class User, with a car property that can be set in a constructor from a supplied variable:

class User {
   public
   function __construct($car) {
      $this - > car = $car;
   }
}

when fetching a record, we should add an array with constructor parameters:

$users = $pdo - > query('SELECT name FROM users LIMIT 1') - > fetchAll(PDO::FETCH_CLASS, 'User', ['Caterpillar']);
$user = $pdo - > query('SELECT name FROM users LIMIT 1') - > fetchObject('User', ['Caterpillar']);

which will will give us

/* object(User)#3 (2) {    ["name"] => string(4) "John"    ["car"]  => string(11) "Caterpillar"} */

With fetchAll() it will be plain and simple,

class User {
   public
   function __construct($car) {
      $this - > car = $car;
   }
}
$stmt = $pdo - > query('SELECT name, car FROM users LIMIT 1');
$users = $stmt - > fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'User', ['Caterpillar']);

while to fetch a single row, we will need to call both setFetchMode() and fetchObject() which is not very convenient.

class User {
   public
   function __construct($car) {
      $this - > car = $car;
   }
}
$stmt = $pdo - > query('SELECT name, car FROM users LIMIT 1');
$stmt - > setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'User');
$user = $stmt - > fetchObject('User', ['Caterpillar']); /*object(User)#3 (2) {  ["car"]  => string(6) "Toyota"  ["name"] => string(4) "John"} */

as you can see, this code is not a summit of efficiency, as we have to write the class name twice. Alternatively, we can use fetch():

$stmt = $pdo - > query('SELECT name, car FROM users LIMIT 1');
$stmt - > setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'User', ['Caterpillar']);
$user = $stmt - > fetch();

There is one more interesting flag which tells PDO to get the class name from the first column's value. With this flag one can avoid using setFetchMode() with fetch():

$data = $pdo - > query("SELECT 'User', name FROM users") - > fetch(PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE); /*object(User)#3 (1) {  ["name"]=> string(4) "John"} */

Besides, as it was noted in the comments to the main article, this mode can be useful if objects of different classes can be created from the same query

class Male {};
class Female {};
$stmt = $pdo - > query('SELECT sex, name FROM users');
$users = $stmt - > fetchAll(PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE); /*array(6) {  [0]=> object(Male)#3 (1) {    ["name"]=> string(4) "John"  }  [1]=> object(Male)#4 (1) {    ["name"]=> string(4) "Mike"  }  [2]=> object(Female)#5 (1) {    ["name"]=> string(4) "Mary"  }  [3]=> object(Female)#6 (1) {    ["name"]=> string(5) "Kathy"  }}*/

Beside creating a new object, PDO can update an existing one. Works with setFetchMode() only, which takes the existing variable as a parameter. Obviously, useless with fetchAll().

class User {
   public $name;
   public $state;
   public
   function __construct() {
      $this - > name = NULL;
   }
}
$user = new User;
$user - > state = "up'n'running";
var_dump($user);
$stmt = $pdo - > query('SELECT name FROM users LIMIT 1');
$stmt - > setFetchMode(PDO::FETCH_INTO, $user);
$data = $stmt - > fetch();
var_dump($data, $user); /*object(Foo)#2 (2) {  ["name"]  => NULL  ["state"] => string(12) "up'n'running"}object(Foo)#2 (2) {  ["name"]  => string(4) "John"  ["state"] => string(12) "up'n'running"}object(Foo)#2 (2) {  ["name"]  => string(4) "John"  ["state"] => string(12) "up'n'running"} */
load more v
40%

PDO - PHP Data Object.,Return Value: TRUE if the method call succeeded, FALSE otherwise.,Return Value: Returns a five-char SQLSTATE as a string, or NULL if there was no operation on the statement handle.,Return Value: An array of the remaining rows in the result set, or false if the method call fails.

Example:

$obj = (object) array('qualitypoint', 'technologies', 'India');
load more v
22%

Data is obtained via the ->fetch(), a method of your statement handle. Before calling fetch, it's best to tell PDO how you'd like the data to be fetched. You have the following options:,support for prepared statements,If you need to pass different data to the constructor for each object, you can set the fetch mode inside the fetch method:,Finally, if you really need to, you can pass arguments to the constructor when fetching data into objects with PDO:

All of these drivers are not necessarily available on your system; here's a quick way to find out which drivers you have:

print_r(PDO::getAvailableDrivers());
load more v
60%

Fetches a row from a result set associated with a PDOStatement object. The fetch_style parameter determines how PDO returns the row. ,PDOStatement::fetch — Fetches the next row from a result set , PDO::FETCH_PROPS_LATE: when used with PDO::FETCH_CLASS, the constructor of the class is called before the properties are assigned from the respective column values. ,PDOStatement::fetchColumn() - Returns a single column from the next row of a result set

PDO::FETCH_ASSOC: Return next row as an array indexed by column name
Array
   (
      [name] => apple[colour] => red
   )

PDO::FETCH_BOTH: Return next row as an array indexed by both column name and number
Array
   (
      [name] => banana[0] => banana[colour] => yellow[1] => yellow
   )

PDO::FETCH_LAZY: Return next row as an anonymous object with column names as properties
PDORow Object
   (
      [name] => orange[colour] => orange
   )

PDO::FETCH_OBJ: Return next row as an anonymous object with column names as properties
kiwi
load more v
48%

There’s a lot of outdated information on the Web that leads new PHP users astray, propagating bad practices and insecure code. PHP: The Right Way is an easy-to-read, quick reference for PHP popular coding standards, links to authoritative tutorials around the Web and what the contributors consider to be best practices at the present time.,This is bad practice for all sorts of reasons, mainly that it’s hard to debug, hard to test, hard to read and it is going to output a lot of fields if you don’t put a limit on there.,Another option is to use the PHP Coding Standards Fixer. It will show which kind of errors the code structure had before it fixed them.,Right now PHP does not support Unicode at a low level. There are ways to ensure that UTF-8 strings are processed OK, but it’s not easy, and it requires digging in to almost all levels of the web app, from HTML to SQL to PHP. We’ll aim for a brief, practical summary.

> php - S localhost: 8000
load more v

Other "fetching-undefined" queries related to "PHP PDO: Fetching data as objects - properties assigned BEFORE __construct is called. Is this correct?"