Symfony2:WHERE子句中的Doctrine子查询,包括LIMIT

时间:2013-06-11 15:20:39

标签: symfony doctrine-orm subquery

我正在尝试将此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行的子查询?

  • Symfony 2.0.15
  • Doctrine 2.1.7
  • PHP 5.3.3
  • MySQL 5.1.52

2 个答案:

答案 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();
}