我正在尝试将此SQL转换为DQL或查询构建器变体的外观。
select *
from project_release r
where (select s.title as status_name
from release_status_log l
left join release_status s
on l.release_status_id = s.id
where l.release_id = r.id
order by l.created_at desc
limit 1
) not in ('Complete', 'Closed')
;
从Release
实体的存储库类内部,我试过了这个
return $this->getEntityManager()->createQuery("
select r.*
from MyBundle:Release r
where (select s.title
from MyBundle:ReleaseStatusLog l
join l.status s
where l.release = r
order by l.createdAt desc
limit 1
) IN ('Complete','Closed')
order by r.release_date ASC
limit 10
")->getArrayResult();
哪个给出了错误
[语法错误]第0行,第265行:错误:预期 Doctrine \ ORM \ Query \ Lexer :: T_CLOSE_PARENTHESIS,得到'限制'
指的是子查询中的limit 1
。
然后我尝试了这个
return $this
->createQueryBuilder('r')
->select('r.*')
->where("(select s.title
from MyBundle:ReleaseStatusLog l
join l.status s
where l.release = r
order by l.created_at desc
limit 1
) $inClause ('Complete', 'Closed')
")
->setMaxResults( $limit )
->orderBy('release_date', 'ASC')
->getQuery()
->getArrayResult()
;
这给出了同样的错误。如何在父查询中执行每行限制为1行的子查询?
答案 0 :(得分:2)
我现在有一个解决方案。我最终回到了原生查询系统,并在问题中使用了实体的结果集映射。
这不是一个很好的解决方案,但它可以工作,直到我看到另一个解决方案,它是这种类型的WHERE子句的唯一选择。
这是我的finder方法现在的样子
/**
* Finds Releases by their current status
*
* @param array $statuses White-list of status names
* @param boolean $blackList Treat $statuses as a black-list
* @param integer $limit Limit the number of results returned
* @param string $order Sort order, ASC or DESC
*
* @throws \InvalidArgumentException
*
* @return array <Release>
*/
public function findByCurrentStatus( array $statuses, $blackList=false, $limit=null, $order='ASC' )
{
if ( empty( $statuses ) )
{
throw new \InvalidArgumentException( "Must provide at least one status" );
}
$inClause = $blackList ? 'not in' : 'in';
$rsm = new ResultSetMappingBuilder($this->getEntityManager());
$rsm->addRootEntityFromClassMetadata('MyBundle:Release', 'r');
$SQL = "
select *
from project_release r
where (select s.title as status_name
from release_status_log l
left join release_status s
on l.release_status_id = s.id
where l.release_id = r.id
order by l.created_at desc
limit 1
) $inClause ('" . implode( "','", $statuses ) . "')
order by r.release_date $order
";
if ( $limit )
{
$SQL .= " limit $limit";
}
return $this
->getEntityManager()
->createNativeQuery( $SQL, $rsm )
->getResult()
;
}
我厌恶回到构建一个字符串的查询,但哦,好吧。哦,对于你的老鹰眼,$statuses
不是来自用户数据,所以这里没有SQL注入漏洞;)
答案 1 :(得分:0)
除了Native SQL解决方案之外,您还可以在单个存储库方法中使用DQL创建两个查询。
可能需要进行一些调整,但您可以尝试这样做:
public function findCompletedReleases()
{
$em = $this->getEntityManager();
$dqlSubQuery = <<<SQL
SELECT
s.title status_name
FROM
Acme\MyBundle\Entity\ReleaseStatus s,
Acme\MyBundle\Entity\ReleaseStatusLog l,
Acme\MyBundle\Entity\Release r
WHERE
l.release = r.id AND
l.status = s.id
ORDER BY l.createdAt DESC
SQL;
$statusName = $em->createQuery($dqlSubQuery)
->setMaxResults(1)
->getSingleScalarResult();
$dql = <<<SQL
SELECT
r
FROM
Acme\MyBundle\Entity\Release r
WHERE
:status_name IN ('Complete','Closed')
ORDER BY r.release_date ASC
SQL;
$q = $em->createQuery($dql)
->setParameters(array('status_name' => $statusName))
->setMaxResults(10);
return $q->getArrayResult();
}