Reputation: 3855
I borrowed the following wrapper class from phpdelusions:
class DB
{
protected static $instance = null;
public static function instance() {
if (self::$instance === null) {
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_CLASS,
PDO::ATTR_EMULATE_PREPARES => FALSE,
);
$dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset='.DB_CHAR;
self::$instance = new PDO($dsn, DB_USER, DB_PASS, $opt);
}
return self::$instance;
}
public static function __callStatic($method, $args) {
return call_user_func_array(array(self::instance(), $method), $args);
}
public static function run($sql, $args = []) {
$stmt = self::instance()->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
I've also set up a users
table with id
, name
, and password
. Now, I am trying to fetch the first row in the table as an object of class User
(assume there is a require_once 'User.php'
somewhere, which loads the model class):
$stmt = DB::run('SELECT * FROM users LIMIT 1');
$user = $stmt->fetchAll(PDO::FETCH_CLASS, 'User'); // Array ( [0] => User Object ( ... ) )
It works, but
fetchAll
always returns an array, and if the query above is somehow corrupted, the result might be a variable number of User
objects, whereas the goal is to always get one, i.e. the first object, even if the query returns multiple rows.PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_CLASS
in instance()
method of DB
which sets the default fetch style to CLASS, I always have to specify PDO::FETCH_CLASS
in fetchAll
, which I feel is redundant.$first = (is_array($user)) ? $user[0] : null;
since you never know if $user
ends up to be empty or not. It would be easier to just fetch the first object and either get a legit row (User
object) or just null. It doesn't make sense to get back an array, if your goal is to get one object, does it?So I tried to change fetchAll
to fetch
with multiple variations, and here is what I got:
$user = $stmt->fetch('User'); // # 1, FETCH_MODE is already CLASS, but no...
// Warning: PDOStatement::fetch() expects parameter 1 to be integer, string given
$user = $stmt->fetch(PDO::FETCH_CLASS, 'User'); // # 2
// Warning: PDOStatement::fetch() expects parameter 2 to be integer, string given
$user = $stmt->fetch(); // #3
// Uncaught PDOException: SQLSTATE[HY000]: General error: No fetch class specified
// so how do you specify it then with fetch() ???
Is it possible to fetch the first row as an object of a given class using fetch
alone? Or do I have no choice but write $stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
and then run fetch
?
Upvotes: 2
Views: 2043
Reputation: 157864
As I see, you are using my PDO wrapper. So you were in one click from the right answer, as there is a sister article Fetching objects with PDO :)
So to make it with fetch(), you will need an extra function call
$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$user = $stmt->fetch();
But, as it is rightfully said in the other answer, fetchObject()
is better in either way:
$user = DB::run('SELECT * FROM users LIMIT 1')->fetchObject('User');
Upvotes: 2
Reputation: 7911
Since you're only wanting 1 object, I can perfectly understand why you don't want to use fetchAll()
. But since you want one, why not use fetchObject()
?
As this is perfectly capable of being used in a while loop as well. Its like the fetch()
for objects.
Returns an instance of the required class with property names that correspond to the column names or FALSE on failure.
public mixed PDOStatement::fetchObject ([ string $class_name = "stdClass" [, array $ctor_args ]] )
Example:
class User{
protected $data = [];
public function __get(string $key){
return $this->data[$key];
}
public function __set(string $key, $value){
$this->data[$key] = $value;
}
public function __construct($a, $b, $c){
echo $a, $b, $c;
print_r($this->data);
}
}
$stmt = DB::run('SELECT id, name FROM users LIMIT 1');
if($user = $stmt->fetchObject('User', [1, 2, 3])){
echo $user->id;
}
Upvotes: 2