如何在Symfony中实现返回多继承模型的查询?
好的,咱们来解决这个问题:你需要在Symfony中实现那条包含ONLY关键字和NATURAL FULL JOIN的SQL查询,获取对应的AdvT1和AdvT2模型实例。结合Doctrine的特性,我给你分享两种实用的实现方式:
方法一:使用原生SQL(完全匹配你的查询逻辑)
因为你的查询用到了PostgreSQL特有的ONLY关键字,以及Doctrine QueryBuilder不支持的NATURAL FULL JOIN语法,所以原生SQL是最直接、最精准的方案。你可以在对应的Repository类里添加如下方法:
方式1:映射结果到实体类
如果你希望直接得到Adv、AdvT1、AdvT2的实体实例,可以用ResultSetMapping来做结果映射:
namespace App\Repository; use App\Entity\Adv; use App\Entity\AdvT1; use App\Entity\AdvT2; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; class AdvRepository extends ServiceEntityRepository { public function __construct(EntityManagerInterface $em) { parent::__construct($em, Adv::class); } public function findAdvT1AndAdvT2(): array { // 直接复用你提供的SQL语句 $sql = 'SELECT * FROM ONLY adv NATURAL FULL JOIN adv_t1 NATURAL FULL JOIN adv_t2 ORDER BY id'; // 创建结果集映射,把查询字段关联到实体属性 $rsm = new ResultSetMapping(); // 映射Adv主表的字段 $rsm->addEntityResult(Adv::class, 'a'); $rsm->addFieldResult('a', 'id', 'id'); $rsm->addFieldResult('a', 'title', 'title'); $rsm->addFieldResult('a', 'price', 'price'); // 映射AdvT1子表的字段(根据你的实际表结构调整字段名) $rsm->addJoinedEntityResult(AdvT1::class, 't1', 'a', 'id'); $rsm->addFieldResult('t1', 'size', 'size'); // 映射AdvT2子表的字段(假设adv_t2有color字段,按需修改) $rsm->addJoinedEntityResult(AdvT2::class, 't2', 'a', 'id'); $rsm->addFieldResult('t2', 'color', 'color'); // 执行原生查询并返回实体结果 $query = $this->getEntityManager()->createNativeQuery($sql, $rsm); return $query->getResult(); } }
方式2:直接获取数组结果
如果不需要实体实例,只是要原始的查询数据,可以简化成下面的代码:
public function findAdvT1AndAdvT2AsArray(): array { $sql = 'SELECT * FROM ONLY adv NATURAL FULL JOIN adv_t1 NATURAL FULL JOIN adv_t2 ORDER BY id'; $connection = $this->getEntityManager()->getConnection(); $stmt = $connection->prepare($sql); $stmt->execute(); // 返回关联数组格式的结果 return $stmt->fetchAllAssociative(); }
方法二:使用Doctrine QueryBuilder(有限替代方案)
Doctrine的QueryBuilder本身不支持FULL JOIN和NATURAL JOIN语法,也不支持PostgreSQL的ONLY关键字。如果你的业务逻辑可以接受用LEFT JOIN替代NATURAL FULL JOIN,可以尝试下面的代码,但要注意这和原SQL的逻辑有差异(原SQL会包含子表中没有对应adv记录的数据,而LEFT JOIN做不到):
public function findAdvT1AndAdvT2WithQueryBuilder(): array { return $this->createQueryBuilder('a') ->select('a, t1, t2') // 按id关联adv_t1表 ->leftJoin(AdvT1::class, 't1', 'WITH', 'a.id = t1.id') // 按id关联adv_t2表 ->leftJoin(AdvT2::class, 't2', 'WITH', 'a.id = t2.id') // 过滤出至少有一个子表关联记录的结果 ->where('t1.id IS NOT NULL OR t2.id IS NOT NULL') ->orderBy('a.id', 'ASC') ->getQuery() ->getResult(); }
总结
- 如果你必须严格遵循原SQL的逻辑(使用
ONLY和NATURAL FULL JOIN),原生SQL是最优选择,它能完全还原你想要的查询行为。 - 如果可以接受查询逻辑的小调整,QueryBuilder能提供更面向对象的操作体验,但要注意功能局限性。
内容的提问来源于stack exchange,提问作者parashutiki
相关产品推荐
相关产品推荐

