note 130118 added to pdostatement.fetchall
| From: | Anonymous | Date: | Wed, 05 Mar 2025 15:31:38 +0000 |
| Subject: | note 130118 added to pdostatement.fetchall | ||
| Groups: | php.notes | ||
| Request: | Send a blank email to php-notes+get-221946@lists.php.net to get a copy of this message | ||
It's important to note that PDO::FETCH_CLASS does not behave the way you would intuitively
expect. Intuitively, you might expect that it either does not call the constructor at all and sets
the properties "magically", or that it simply calls the constructor with the data from the
database. Both assumptions are incorrect. It *does* call the constructor, but *not* with data from
the database; it uses "magic" to set the properties. This can result in problems.
For example, one might intuitively expect code like this to work, perhaps as a starting point for
doing Domain-Driven Design without a full-fat ORM. (It doesn't.)
<?php
final class Example{
private static ?PDO $pdo;
private function __construct(
public readonly int $id,
public readonly string $val1,
public readonly ?string $val2,
){}
public static function init(PDO $pdo){
static::$pdo = $pdo;
static::$pdo->exec("
CREATE TABLE IF NOT EXISTS example (
id INT AUTO_INCREMENT PRIMARY KEY,
val1 TEXT NULL,
val2 TEXT NOT NULL
)
");
}
public static function create(string $val1, ?string $val2=null){
if (!isset(static::$pdo)) throw new RuntimeException('Not yet initialized');
$query = static::$pdo->prepare("
INSERT INTO example
(val1, val2)
VALUES
(?, ?)
");
$query->execute([$val1, $val2]);
return static::fetch(static::$pdo->lastInsertId());
}
public static function fetch($id){
if (!isset(static::$pdo)) throw new RuntimeException('Not yet initialized');
$query = static::$pdo->prepare("SELECT * FROM example WHERE id = ?");
$query->execute([$id]);
$result = $query->fetchAll(PDO::FETCH_CLASS, 'Example');
if (!empty($result)) return $result[0];
}
}
?>
For a pattern like this to work, you'll instead need to do one of the following:
1. Use PDO::FETCH_FUNC instead of PDO::FETCH_CLASS, and pass it an anonymous function that calls the
constructor.
2. Have a constructor that does nothing, and use the create() method to set properties directly.
This would require removing the readonly modifier (though you could use PHP's new asymmetric
visibility instead to achieve a similar effect). This also means that you can no longer actually use
the constructor to fully construct an object (which, depending on your needs, may or may not be an
acceptable compromise). You'll need a private constructor to avoid having partially-constructed
objects floating around.
3. Use PDO::FETCH_ASSOC instead of PDO::FETCH_CLASS, then use
ReflectionClass::newInstanceWithoutConstructor(), ReflectionObject::getProperty(), and
ReflectionProperty::setValue() to implement "magical" class construction yourself.
4. Give up and use Doctrine or some other ORM.
There is, unfortunately, no solution that doesn't either have some compromise, require writing
boilerplate code, or require adding a dependency to your project.
----
Server IP: 45.112.84.4
Probable Submitter: 45.112.84.18 (proxied: 163.116.147.130)
----
Manual Page -- https://php.net/manual/en/pdostatement.fetchall.php
Edit -- https://main.php.net/note/edit/130118
Del: integrated -- https://main.php.net/note/delete/130118/integrated
Del: useless -- https://main.php.net/note/delete/130118/useless
Del: bad code -- https://main.php.net/note/delete/130118/bad+code
Del: spam -- https://main.php.net/note/delete/130118/spam
Del: non-english -- https://main.php.net/note/delete/130118/non-english
Del: in docs -- https://main.php.net/note/delete/130118/in+docs
Del: other reasons-- https://main.php.net/note/delete/130118
Reject -- https://main.php.net/note/reject/130118
Search -- https://main.php.net/manage/user-notes.php