如何使用PDO::FETCH_CLASS将GROUP_CONCAT结果映射为对象数组属性
问题原因说明
PDO::FETCH_CLASS模式的属性赋值规则是:如果目标类中已经声明了对应名称的属性(无论访问修饰符是public、protected还是private),PDO会直接向该属性写入值,不会触发魔术方法__set()。只有当赋值的属性未在类中声明时,才会调用__set(),这就是你之前的方案失效的核心原因。
可行方案(均不需要自行遍历结果集)
方案1:依赖__set()触发逻辑(改动最小)
删除类中提前声明的$referencingNames属性,PDO给不存在的属性赋值时就会自动触发__set(),示例代码如下:
class someObject { public int $id; public ?array $referencingNames = null; public function __set($name, $value) { if($name == "referencingNames") { $this->referencingNames = explode(",", $value); return; } $this->$name = $value; } }
原有查询代码不需要修改:
$objects = $statement->fetchAll(PDO::FETCH_CLASS, someObject::class);
方案2:使用PDO::FETCH_FUNC自定义映射(更直观易维护)
这种方式不需要依赖魔术方法的黑盒逻辑,所有转换规则都显式定义,灵活性更高:
目标类保持原有定义即可:
class someObject { public int $id; public array $referencingNames; }
查询时传入自定义处理回调:
$objects = $statement->fetchAll(PDO::FETCH_FUNC, function(int $id, string $referencingNames) { $obj = new someObject(); $obj->id = $id; $obj->referencingNames = explode(',', $referencingNames); return $obj; });
方案3:使用FETCH_PROPS_LATE配合构造函数
如果你需要保留所有属性的显式声明,可以搭配PDO::FETCH_PROPS_LATE参数使用,该参数会让PDO先调用类的构造函数,再进行属性赋值:
class someObject { public int $id; public array $referencingNames; private string $rawNames; public function __construct() { // 构造函数执行后PDO会完成属性赋值,转换逻辑在__set中实现 } public function __set($name, $value) { if ($name === 'referencingNames') { $this->rawNames = $value; $this->referencingNames = explode(',', $value); return; } $this->$name = $value; } }
查询时添加参数:
$objects = $statement->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, someObject::class);
推荐优先使用方案2,逻辑完全透明,后续调整转换规则时不需要修改类定义,适配性更高。
内容的提问来源于stack exchange,提问作者helle
相关产品推荐
相关产品推荐

