Symfony 5.1中如何在Doctrine查询中使用FIND_IN_SET与子查询?
解决Symfony 5.1中Doctrine使用FIND_IN_SET结合子查询的语法错误问题
核心问题原因
Doctrine的DQL(Doctrine Query Language)并非完全兼容原生SQL语法,直接在DQL中使用FIND_IN_SET会因为函数未注册报错,同时子查询的写法也需符合DQL规范,不能直接嵌套原生SQL字符串。
解决方案步骤
1. 注册FIND_IN_SET自定义DQL函数
在config/packages/doctrine.yaml中添加自定义函数配置,让Doctrine识别FIND_IN_SET:
doctrine: orm: entity_managers: default: dql: string_functions: FIND_IN_SET: DoctrineExtensions\Query\Mysql\FindInSet
注:如果没有安装doctrine-extensions,先通过Composer安装:
composer require beberlei/doctrineextensions
2. 正确编写带FIND_IN_SET的查询(以筛选属性为例)
方式一:使用DQL子查询
use Doctrine\ORM\EntityManagerInterface; use App\Entity\Product; use App\Entity\ProductAttribute; // 获取EntityManager $em = $this->getDoctrine()->getManager(); // 子查询:获取拥有目标属性的产品ID集合 $subQuery = $em->createQueryBuilder() ->select('pa.product') ->from(ProductAttribute::class, 'pa') ->where('pa.attribute = :attributeId') ->getDQL(); // 主查询:筛选出符合条件的产品 $query = $em->createQueryBuilder() ->select('p') ->from(Product::class, 'p') ->where(sprintf('FIND_IN_SET(p.id, (%s)) > 0', $subQuery)) ->setParameter('attributeId', $targetAttributeId) ->getQuery(); $products = $query->getResult();
方式二:使用原生SQL查询(贴近原生命令行SQL)
如果DQL写法受限,直接用原生SQL更直接:
$targetAttributeId = 123; // 替换为实际筛选的属性ID $sql = " SELECT p.* FROM product p WHERE FIND_IN_SET(p.id, ( SELECT pa.product_id FROM product_attribute pa WHERE pa.attribute_id = :attributeId )) > 0 "; $stmt = $em->getConnection()->prepare($sql); $stmt->execute(['attributeId' => $targetAttributeId]); $products = $stmt->fetchAllAssociative();
3. 扩展为多属性筛选(类似亚马逊多选项筛选)
如果要支持多个属性同时筛选(比如同时选颜色和尺寸),可以调整子查询为分组统计:
$attributeIds = [123, 456]; // 多个属性ID $subQuery = $em->createQueryBuilder() ->select('pa.product') ->from(ProductAttribute::class, 'pa') ->where('pa.attribute IN (:attributeIds)') ->groupBy('pa.product') ->having('COUNT(DISTINCT pa.attribute) = :attributeCount') ->getDQL(); $query = $em->createQueryBuilder() ->select('p') ->from(Product::class, 'p') ->where(sprintf('FIND_IN_SET(p.id, (%s)) > 0', $subQuery)) ->setParameter('attributeIds', $attributeIds) ->setParameter('attributeCount', count($attributeIds)) ->getQuery(); $products = $query->getResult();
关键注意事项
- 确保所有实体映射关系正确,特别是
Product与ProductAttribute的一对多关联配置无误。 FIND_IN_SET是MySQL特有函数,若切换数据库需调整对应函数。- 大数量数据下,建议为
product_attribute.product_id和product_attribute.attribute_id建立联合索引,提升查询性能。
内容的提问来源于stack exchange,提问作者Mike Zhang
相关产品推荐
相关产品推荐

