note 130118 added to pdostatement.fetchall

From: 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

« previous php.notes (#221946) next »