【问题标题】:Custom DQL functions in doctrine2学说 2 中的自定义 DQL 函数
【发布时间】:2014-09-08 02:44:20
【问题描述】:

如何在部分中使用自定义 DQL 函数来水合查询结果。

寻找一种在 createQuery 或 任何其他学说方式(nativeSql、QueryBuilder)中使用 DQL 函数的方法,它不需要使用部分,但它应该根据表的关系和它的关系来水合我的数组应该只选择选择性字段

以下查询工作正常:

$q = $em->createQuery("select "
                . "partial t.{id, description}, "
                . "partial ut.{id, firstName, lastName, email}, "
                . "DATE_FORMAT(ut.created, '%m-%d-Y') "
                . "from PMIUserBundle:Task t LEFT JOIN t.users ut");

DATE_FORMAT(ut.created, '%m-%d-Y') 在不完整时可以正常工作。

DATE_FORMAT 已经在 config.yml 中注册为自定义函数

config.yml:

dql:
            datetime_functions:
                DATE_FORMAT: PMI\UserBundle\DoctrineFunctions\DateFormat

以下查询会产生问题:

 $q = $em->createQuery("select "
                . "partial t.{id, description}, "
                . "partial ut.{id, firstName, lastName, email, DATE_FORMAT(created, '%m-%d-Y')}, "
                . "DATE_FORMAT(ut.created, '%m-%d-Y') "
                . "from PMIUserBundle:Task t LEFT JOIN t.users ut");

给出错误:

[Syntax Error] line 0, col 91: Error: Expected Doctrine\ORM\Query\Lexer::T_CLOSE_CURLY_BRACE, got '(' 

以下是我的 DateFormat 类:

class DateFormat extends \Doctrine\ORM\Query\AST\Functions\FunctionNode {



    protected $dateExpression;

    protected $formatChar;

    public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) {

         return 'DATE_FORMAT('.
                $sqlWalker->walkArithmeticExpression($this->dateExpression) .
                ','.
                $sqlWalker->walkStringPrimary($this->formatChar).
                ')';



    }

    public function parse(\Doctrine\ORM\Query\Parser $parser) {

        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);

        $this->dateExpression = $parser->ArithmeticExpression();
        $parser->match(Lexer::T_COMMA);


        $this->formatChar = $parser->StringPrimary();
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);


    }

}

编辑:

好的,所以 Partial 不允许我使用 DQL 函数。感谢FuzzyTree 在我的实体类中使用ut->getFormattedCreatedDate() 的解决方案。但我仍然很好奇,如果我们对子表进行分组并使用 mysql 聚合函数计算一些数量会怎样。

如何使用 createQuery、queryBuilder、nativeSQl 或任何其他方式在学说 2 中实现以下条件:

  1. 仅选择选择性字段
  2. 使用聚合函数按子表实体分组
  3. 基于表关系的水合物数组,聚合函数结果 应该在子数组中。

每次如果我们使用 DQL 函数,我们是否必须像我为 DateFormat 创建的那样创建类?如果我们使用嵌套的 MySql 函数,比如

GROUP_CONCAT(DISTINCT CONCAT(tags.id,',',tags.displayName)

感谢您阅读我的问题。

【问题讨论】:

    标签: php mysql symfony doctrine-orm php-5.4


    【解决方案1】:

    如何在部分中使用自定义 DQL 函数来水合查询 结果。

    partial 关键字告诉教义返回仅部分水合的实体,并允许您指定要水合的字段。

    在检索部分实体时,您只能选择实体的字段(否则,学说会在哪里“存储”您选择的字段?),因此无法将 DQL 函数指定为部分的字段对象。

    【讨论】:

    • @hardik 你能在你的users 实体中添加一个方法getFormattedCreatedDate 来处理php 端的格式化吗?那么您可以使用partial ut.{id, firstName, lastName, email, created} 并致电ut->getFormattedCreatedDate()
    【解决方案2】:

    这个怎么样?

        $qb = $this->_em->createQueryBuilder()
                ->select( 't.id, t.description, '
                        . 'ut.id, ut.firstName, '
                        . 'ut.lastName, ut.email, '
                        . 'DATE_FORMAT( ut.created, \'%m-%d-Y\' )' )
                ->from( 'PMIUserBundle:Task', 't' )
                ->leftjoin( 't.users', 'ut' );
    
        $result = $qb->getQuery()->getResult();
    

    假设您在 Repository 类中使​​用它(这就是调用 $this->_em 的原因)。

    我注意到关于自定义函数的另外两件事:

    1. 我个人将它定义为“string_functions:”而不是在 config.yml 文件中
    2. 我知道至少有一个 Bundle 实现了这个自定义功能:https://github.com/beberlei/DoctrineExtensions

    希望这有帮助,

    【讨论】:

      【解决方案3】:

      不支持

      PARTIAL ut.{id, firstName, lastName, email, DATE_FORMAT(created, '%m-%d-Y')}
      

      支持像这样在部分实体的选择中使用 DQL 函数。

      这也没有多大意义。在这种情况下,您(可能)已将字段 created 映射为 datetime 字段,因此您将期望其中包含完整的 DateTime 值。但现在你突然只是使用日期部分。而且情况变得更糟:您以这样一种方式格式化了值,导致无法创建 DateTime 对象(它需要 Y-m-d H:i:s,而 date 类型需要 Y-m-d)。

      对于任何部分选择:您不能并且不应该更改字段的值。

      另类

      您可以只使用数组,而不是水合为部分实体。使用$q->getArrayResult()$q->getScalarResult(),以更适合您的目的使用。

      如果您愿意,可以在 Repository 方法中对结果进行分组/嵌套。示例:

      public function getSomeData()
      {
          $result = array();
      
          $dql = <<<EOQ
      SELECT
      t.id AS t_id,
      t.description AS t_description,
      u.id AS u_id,
      u.firstName AS u_firstName,
      u.lastName AS u_lastName,
      u.email AS u_email,
      DATE_FORMAT(u.created, '%m-%d-Y') AS u_created
      FROM PMIUserBundle:Task t
      LEFT JOIN t.users u
      EOQ;
      
          $q = $this->getEntityManager()->createQuery($dql);
      
          foreach ($q->getScalarResult() as $row) {
              if (!isset($result[$row['t_id']])) {
                  $result[$row['t_id']] = array(
                      'id'          => $row['t_id'],
                      'description' => $row['t_description'],
                      'users'       => array()
                  );
      
                  $result[$row['t_id']]['users'][] = array(
                      'u.id'        => $row['u_id'],
                      'u.firstName' => $row['u_firstName'],
                      'u.lastName'  => $row['u_lastName'],
                      'u.email'     => $row['u_email'],
                      'u.created'   => $row['u_created']
                  );
              }
          }
      
          return $result;
      }
      

      嵌套 DQL 函数

      嵌套 DQL 函数应该没问题(如果你写得正确的话)。并且开箱即可支持DISTINCT

      【讨论】:

      • 感谢您的回复。 $q->getArrayResult() 或 $q->getScalarResult() 将根据表关系对数组进行水合物。
      • 这不是答案;而是一个想法。您能否将相关数据复制到临时表中,然后针对该表进行部分选择?
      • 我添加了一个示例,用于在使用 getScalarResult() 时对结果进行分组/嵌套。
      猜你喜欢
      • 1970-01-01
      • 2012-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多