在日常使用 PHP 和 PDO 操作数据库时,PDOStatement::fetchObject 是一个非常方便的方法,它可以直接将查询结果转成对象。然而,默认情况下它只会创建,不会自动处理更复杂的。如果你希望结果中包含嵌套的对象,比如一个 User 对象里面嵌套一个 Profile 对象,就需要稍微自己动手加工一下。
下面我们就一步步来看,如何实现这一目标。
假设你有这样两张表:
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100),
profile_id INT
);
CREATE TABLE profiles (
id INT PRIMARY KEY,
bio TEXT,
avatar_url VARCHAR(255)
);
你希望查询出用户数据,并且让结果变成一个 User 对象,同时嵌套一个 Profile 对象。
例如希望得到类似这样的结构:
User {
id => 1,
name => 'Alice',
profile => Profile {
id => 2,
bio => 'Hello world!',
avatar_url => 'https://gitbox.net/uploads/avatar.jpg'
}
}
我们需要为 User 和 Profile 创建 PHP 类:
class Profile {
public int $id;
public string $bio;
public string $avatar_url;
}
class User {
public int $id;
public string $name;
public Profile $profile;
}
你可以使用 JOIN 查询,把两个表的数据一次性取出:
$sql = "
SELECT
u.id AS user_id,
u.name AS user_name,
p.id AS profile_id,
p.bio AS profile_bio,
p.avatar_url AS profile_avatar_url
FROM users u
JOIN profiles p ON u.profile_id = p.id
";
$stmt = $pdo->query($sql);
由于 fetchObject 默认只映射一层数据,我们需要自己动手组装嵌套对象。可以这样做:
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$user = new User();
$user->id = (int) $row['user_id'];
$user->name = $row['user_name'];
$profile = new Profile();
$profile->id = (int) $row['profile_id'];
$profile->bio = $row['profile_bio'];
$profile->avatar_url = $row['profile_avatar_url'];
$user->profile = $profile;
// 现在 $user 就是包含嵌套 Profile 的对象了
var_dump($user);
}
如果你经常需要这样处理,可以封装成一个小工具函数:
function mapRowToUser(array $row): User {
$user = new User();
$user->id = (int) $row['user_id'];
$user->name = $row['user_name'];
$profile = new Profile();
$profile->id = (int) $row['profile_id'];
$profile->bio = $row['profile_bio'];
$profile->avatar_url = $row['profile_avatar_url'];
$user->profile = $profile;
return $user;
}
// 使用
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$user = mapRowToUser($row);
var_dump($user);
}
虽然 PDOStatement::fetchObject 本身不能直接创建嵌套对象,但通过手动赋值和封装,可以非常灵活地实现复杂的数据结构映射。这种方式比简单的数组处理更清晰、类型更安全,也更符合现代 PHP 面向对象的开发习惯。
如果想进一步简化,可以考虑结合 ORM 工具(比如 Eloquent、Doctrine)来自动处理这些映射,但对于轻量项目,这种手动映射方法已经非常够用了!