PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
151: PDO Fetch Modes and Statement Options
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:
- Create a
Productclass with public properties that match the database columns. - Add a method to the
Productclass calledgetAvailability()that returns a string: "In Stock" ifstock_countis greater than 0, and "Out of Stock" otherwise. - Write a PDO script that fetches a single product by its ID using
PDO::FETCH_CLASS. - Call the
getAvailability()method on the resulting object and echo the result.
There are no comments for now.