Skip to Content
Course content

151: PDO Fetch Modes and Statement Options

Click on the "Edit" button in the top corner of the screen to edit your slide content.

By now, you're comfortable connecting to a database and running queries. But if you've just been using the default settings, you're probably dealing with a lot of "magic arrays" where you have to remember exactly how the column names are spelled in the database to get your data out. It works, but it's fragile.

I want to show you how to change how PDO hands that data back to you. We're going to build a simple User Profile fetcher to see the different fetch modes in action.

The familiar associative array approach

Most people start with PDO::FETCH_ASSOC. It's the bread and butter of PHP database work. Let's look at a basic fetch for a user by their ID.

$stmt = $pdo->prepare("SELECT id, username, email FROM users WHERE id = ?");
$stmt->execute([1]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

echo $user['username']; // Easy enough, but it's just a string key.

This is fine for a quick script, but in a larger application, I hate relying on string keys. One typo like $user['user_name'] instead of $user['username'] and you've got a silent bug or a notice. I prefer something a bit more structured.

Moving toward objects for cleaner code

If you want to move away from arrays, PDO::FETCH_OBJ is the next step. It returns an anonymous stdClass object. It doesn't give you custom logic, but the syntax is cleaner.

$stmt = $pdo->prepare("SELECT id, username, email FROM users WHERE id = ?");
$stmt->execute([1]);
$user = $stmt->fetch(PDO::FETCH_OBJ);

echo $user->username; // Now it feels like a real object.

Still, this is just a data container. We can't add methods to a stdClass. If I want a method like getFullName() or isAdmin(), I need a real class.

Mapping results directly to a custom User class

This is where PDO::FETCH_CLASS becomes incredibly powerful. Instead of fetching a generic object and then manually mapping it to a class, PDO can inject the database values directly into your class properties.

class User {
    public $id;
    public $username;
    public $email;

    public function getContactInfo() {
        return "Contact {$this->username} at {$this->email}";
    }
}

$stmt = $pdo->prepare("SELECT id, username, email FROM users WHERE id = ?");
$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$stmt->execute([1]);
$user = $stmt->fetch();

echo $user->getContactInfo();

Notice I used setFetchMode() on the statement itself. This tells PDO: "Every time I call fetch() on this specific statement, I want a User object."

Wait, why is my constructor acting weird?

Here is where I tripped up when I first learned this. I tried to add a constructor to the User class to initialize some default values, like this:

class User {
    public $id;
    public $username;
    public $role = 'guest';

    public function __construct() {
        // I thought I'd set a default here
        $this->role = 'member'; 
    }
}

I expected that the constructor would run, and then PDO would fill in the database values. But I noticed my $role was getting overwritten or behaving unexpectedly. I discovered that PDO assigns the property values from the database BEFORE it calls the constructor. If you have a property in your database and you also set it in your constructor, the constructor wins and overwrites the database value.

To fix this, if you need the constructor to run before the data is mapped, you can pass a third argument to setFetchMode or use the PDO::FETCH_PROPS_LATELY flag. But usually, the simplest fix is just to be mindful that the properties are already populated by the time __construct() hits.

Cleaning up with global statement options

Tired of typing PDO::FETCH_ASSOC or setFetchMode every single time? You can set a global default for the entire connection. I usually do this right when I instantiate the PDO object.

$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
];

$pdo = new PDO($dsn, $user, $pass, $options);

By setting ATTR_DEFAULT_FETCH_MODE, you can now just call $stmt->fetch() without any arguments, and it will default to associative arrays. It keeps the code much leaner.




📋 Practical Task

Exercise: Implementing a Product Model Mapper

You have a database table called products with the columns id, product_name, price, and stock_count.

Your task is to:

  1. Create a Product class with public properties that match the database columns.
  2. Add a method to the Product class called getAvailability() that returns a string: "In Stock" if stock_count is greater than 0, and "Out of Stock" otherwise.
  3. Write a PDO script that fetches a single product by its ID using PDO::FETCH_CLASS.
  4. Call the getAvailability() method on the resulting object and echo the result.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.