如何用Doctrine原生SQL与ResultSet映射查询结果到ContactDTO?
可行,具体实现方案如下
完全可以通过Doctrine原生SQL结合ResultSetMapping将UNION查询结果直接映射到ContactDTO,以下是具体实现步骤:
1. 配置ResultSetMapping映射规则
根据你的ContactDTO结构,有两种常见映射方式,可按需选择:
方式一:用ConstructorResult自动映射(推荐)
如果你的ContactDTO定义了接收所有属性的构造函数,Doctrine可以自动实例化DTO并赋值:
// ContactDTO.php class ContactDTO { public function __construct( public int $id, public string $type, public string $firstname, public string $lastname, public string $email ) {} }
查询时配置映射规则并执行:
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\ResultSetMappingBuilder; public function getCombinedContacts(EntityManagerInterface $em): array { $rsm = new ResultSetMappingBuilder($em); // 绑定DTO类与查询字段的映射关系 $rsm->addConstructorResult( ContactDTO::class, [ $rsm->addFieldResult('dto', 'id', 'id'), $rsm->addFieldResult('dto', 'type', 'type'), $rsm->addFieldResult('dto', 'firstname', 'firstname'), $rsm->addFieldResult('dto', 'lastname', 'lastname'), $rsm->addFieldResult('dto', 'email', 'email'), ] ); // 替换为你实际带条件的UNION查询 $sql = <<<SQL SELECT id, 'contact1' AS type, firstname, lastname, email FROM contact1 WHERE status = :active UNION SELECT id, 'contact2' AS type, firstname, lastname, email FROM contact2 WHERE status = :active SQL; $query = $em->createNativeQuery($sql, $rsm); // 绑定参数,避免SQL注入 $query->setParameter('active', 1); // 直接返回ContactDTO实例数组 return $query->getResult(); }
方式二:标量结果手动转换
如果DTO没有构造函数,也可以先获取标量数组结果,再手动映射:
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\ResultSetMapping; public function getCombinedContacts(EntityManagerInterface $em): array { $rsm = new ResultSetMapping(); // 定义查询返回的标量字段别名 $rsm->addScalarResult('id', 'id'); $rsm->addScalarResult('type', 'type'); $rsm->addScalarResult('firstname', 'firstname'); $rsm->addScalarResult('lastname', 'lastname'); $rsm->addScalarResult('email', 'email'); $sql = <<<SQL SELECT id, 'contact1' AS type, firstname, lastname, email FROM contact1 WHERE ... UNION SELECT id, 'contact2' AS type, firstname, lastname, email FROM contact2 WHERE ... SQL; $query = $em->createNativeQuery($sql, $rsm); $scalarResults = $query->getResult(); // 手动转换为DTO数组 return array_map(function(array $row) { $dto = new ContactDTO(); $dto->id = $row['id']; $dto->type = $row['type']; $dto->firstname = $row['firstname']; $dto->lastname = $row['lastname']; $dto->email = $row['email']; return $dto; }, $scalarResults); }
2. 关键注意事项
- 确保原生SQL返回的字段别名与DTO的属性名完全匹配,若别名不同,可通过
addFieldResult指定映射,比如$rsm->addFieldResult('dto', 'contact_id', 'id') - 使用ConstructorResult时,DTO的构造函数参数顺序要与映射的字段顺序一致
- 复杂子查询、分组、WHERE条件不影响映射,只要最终返回的字段符合映射规则即可
- 所有外部参数必须用
setParameter绑定,避免SQL注入
内容的提问来源于stack exchange,提问作者LinChan
相关产品推荐
相关产品推荐

