Symfony跨关联表查询指定状态订单商品的DQL写法问题
问题背景
需要查询匹配Orders表中state字段值的关联Items数据,涉及3张数据表:
- Items表
- Orders表:存储订单编号、状态等字段,与OrderDetails为OneToMany关联关系,与Items为ManyToOne关联关系
- OrderDetails表:存储单条订单的明细数据,包含id_item、orders_id等字段,与Orders为ManyToOne关联关系,与Items为ManyToOne关联关系
表关联逻辑:Order(携带state字段) -> OrderDetails(关联对应items)
原有写法问题
错误的findBy调用
$order = $this->entityManager->getRepository(OrderDetails::class)->findBy(['items => $id_item, 'Order.state' => 2], ['id' => 'desc']);
Doctrine默认的findBy方法不支持直接通过点语法跨多层关联过滤字段,且代码里'items缺少单引号闭合,语法本身就有错误。
错误的DQL写法
$query = $entityManager->createQuery( 'SELECT i.id, c.state_id, i.itemno, o.quantity, u.code, u.id AS idcustomer, c.customers_id JOIN i.Items o, o.Order c, c.Customers u FROM App\Entity\Items AS i, App\Entity\Order AS c, App\Entity\OrderDetails AS o, App\Entity\Customers AS u WHERE c.state_id IN (:ostate) AND i.id IN (:items_id) AND c.customers_id IN (u.id) ' )->setParameter('items_id', $items_id) ->setParameter('ostate', $ostate); return $query->getResult();
存在3个核心问题:
- 语法顺序错误:DQL要求
FROM必须写在JOIN之前 - 关联逻辑错误:DQL的JOIN基于实体类中定义的关联属性,不需要在FROM中重复声明所有关联表,也不能用逗号分隔多个JOIN规则
- 字段名错误:DQL面向实体属性编写,不是面向数据库表字段,不能直接写数据库的下划线命名字段(如
state_id),要对应实体里的属性名(如stateId)
正确实现方案
推荐两种可用写法,优先选择Repository封装的方式,符合Symfony最佳实践。
写法1:Repository自定义查询
在OrderDetailsRepository中添加查询方法,以中间明细表为入口做关联查询:
public function findMatchedItems(array $itemsId, array $orderState): array { $qb = $this->createQueryBuilder('o'); return $qb->select('i.id', 'c.stateId', 'i.itemno', 'o.quantity', 'u.code', 'u.id AS idcustomer', 'c.customersId') ->join('o.order', 'c') ->join('o.items', 'i') ->join('c.customers', 'u') ->where('c.stateId IN (:ostate)') ->andWhere('i.id IN (:items_id)') ->setParameter('ostate', $orderState) ->setParameter('items_id', $itemsId) ->orderBy('o.id', 'desc') ->getQuery() ->getResult(); }
调用时直接在控制器中注入Repository,传参即可:
$result = $this->orderDetailsRepository->findMatchedItems($items_id, $ostate);
写法2:直接写DQL(临时使用)
如果不想封装Repository方法,直接修正DQL语法即可:
$query = $entityManager->createQuery( 'SELECT i.id, c.stateId, i.itemno, o.quantity, u.code, u.id AS idcustomer, c.customersId FROM App\Entity\OrderDetails o JOIN o.order c JOIN o.items i JOIN c.customers u WHERE c.stateId IN (:ostate) AND i.id IN (:items_id) ORDER BY o.id DESC ' )->setParameter('items_id', $items_id) ->setParameter('ostate', $ostate); $result = $query->getResult();
注意事项
- 代码中
o.order、o.items、c.customers必须和实体类内定义的关联属性名完全一致,比如OrderDetails实体中关联Orders的属性叫$order就写o.order,不要直接写数据库表名 - 如果需要返回Items实体对象而非字段数组,把SELECT子句改成
SELECT i即可,过滤条件保持不变 - 禁止使用FROM多表+WHERE条件的隐式连表写法,会生成笛卡尔积,性能差且容易返回错误数据
内容的提问来源于stack exchange,提问作者user331157
相关产品推荐
相关产品推荐

