如何用Doctrine 2.5的createQuery()替代原生MySQL查询实现驼峰式结果?
迁移到Doctrine DQL实现驼峰式结果水合
完全理解你的需求——想要利用Doctrine自动将下划线列名转为驼峰式结果的特性,同时把原生MySQL查询迁移到DQL。下面我来详细拆解可行的方案,包括你计划的DQL迁移,以及其他替代选项:
一、为什么选择DQL(createQuery)是最优解?
Doctrine ORM默认使用UnderscoreNamingStrategy,它会自动完成数据库下划线列名和实体驼峰属性名的双向映射。当你用createQuery执行DQL时,查询结果会自动水合为实体对象(或驼峰键名的数组),完全符合你的需求。
简单DQL示例
假设你有一个User实体,数据库列是user_id、full_name,实体属性对应userId、fullName:
// 初始化EntityManager $entityManager = ...; // 编写DQL查询 $query = $entityManager->createQuery('SELECT u FROM App\Entity\User u WHERE u.userId = :id'); $query->setParameter('id', 123); // 水合为实体对象(属性为驼峰) $user = $query->getSingleResult(); echo $user->getFullName(); // 输出对应full_name列的值 // 或者水合为驼峰键名的数组 $userArray = $query->getSingleResult(\Doctrine\ORM\Query::HYDRATE_ARRAY); echo $userArray['fullName']; // 同样输出对应值
迁移原生查询到DQL的注意事项
- 原生MySQL函数(比如
DATE_FORMAT)需要替换为DQL支持的函数,或者自定义DQL函数扩展; - 关联查询直接使用DQL的关联语法(比如
JOIN u.addresses a),无需手动写JOIN语句; - 复杂查询可以配合
QueryBuilder(本质还是DQL),可读性和维护性更好:
$qb = $entityManager->createQueryBuilder() ->select('u', 'a') ->from(App\Entity\User::class, 'u') ->leftJoin('u.addresses', 'a') ->where('u.userId = :id') ->setParameter('id', 123); $users = $qb->getQuery()->getResult();
二、其他替代方案(无需全量迁移DQL)
如果有部分原生查询暂时无法迁移到DQL,以下方案也能实现下划线到驼峰的结果转换:
1. 自定义结果集水合器
创建一个自定义水合器,专门处理原生查询结果的下划线转驼峰:
use Doctrine\ORM\Internal\Hydration\AbstractHydrator; class CamelCaseArrayHydrator extends AbstractHydrator { protected function hydrateAllData(): array { $result = []; while ($row = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) { $camelCaseRow = []; foreach ($row as $key => $value) { // 下划线转驼峰逻辑 $camelCaseKey = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $key)))); $camelCaseRow[$camelCaseKey] = $value; } $result[] = $camelCaseRow; } return $result; } }
注册并使用这个水合器:
// 注册自定义水合模式 $entityManager->getConfiguration()->addCustomHydrationMode( 'CAMEL_CASE_ARRAY', CamelCaseArrayHydrator::class ); // 执行原生查询并使用自定义水合 $rsm = new \Doctrine\ORM\Query\ResultSetMapping(); $rsm->addScalarResult('user_id', 'userId'); $rsm->addScalarResult('full_name', 'fullName'); $nativeQuery = $entityManager->createNativeQuery( 'SELECT user_id, full_name FROM users WHERE user_id = ?', $rsm ); $nativeQuery->setParameter(1, 123); // 获取驼峰式结果数组 $user = $nativeQuery->getSingleResult('CAMEL_CASE_ARRAY');
2. 手动转换查询结果
写一个工具函数,在获取原生查询结果后手动转换下划线键为驼峰:
function underscoreToCamelCase(array $data): array { $result = []; foreach ($data as $key => $value) { $camelCaseKey = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $key)))); // 递归处理嵌套数组 $result[$camelCaseKey] = is_array($value) ? underscoreToCamelCase($value) : $value; } return $result; } // 使用示例 $connection = $entityManager->getConnection(); $stmt = $connection->executeQuery('SELECT user_id, full_name FROM users WHERE user_id = ?', [123]); $rawRow = $stmt->fetchAssociative(); $camelCaseRow = underscoreToCamelCase($rawRow); // $camelCaseRow 结果为 ['userId' => 123, 'fullName' => 'xxx']
3. 确认实体映射的命名策略
如果你的实体映射配置正确,Doctrine会自动处理命名转换。检查实体的@Column注解,确保name属性对应数据库的下划线列名:
namespace App\Entity; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] class User { #[ORM\Id] #[ORM\Column(name: 'user_id', type: 'integer')] private $userId; #[ORM\Column(name: 'full_name', type: 'string')] private $fullName; // getter/setter... }
只要映射正确,无论是DQL还是QueryBuilder的结果都会自动是驼峰式。
总结
- 优先推荐迁移到DQL/QueryBuilder:这能充分利用Doctrine ORM的特性(比如实体生命周期、关联管理、查询缓存),同时原生支持驼峰式结果;
- 特殊场景用替代方案:如果有复杂原生查询难以迁移,自定义水合器或手动转换是不错的补充。
内容的提问来源于stack exchange,提问作者felipetnh
相关产品推荐
相关产品推荐

