【问题标题】:Using DQL functions inside Doctrine 2 ORDER BY在 Doctrine 2 ORDER BY 中使用 DQL 函数
【发布时间】:2014-09-06 02:22:00
【问题描述】:

我正在使用 MySQL 数据库在 Symfony 2.3 中使用 Doctrine 2.4 做一个项目。

我有一个 FieldValue 实体(简体):

class FieldValue
{
    /**
     * The ID
     *
     * @var integer
     */
    protected $fieldValueId;

    /**
     * Id of associated Field entity
     *
     * @var integer
     */
    protected $fieldId;

    /**
     * Id of associated user
     *
     * @var integer
     */
    protected $userId;

    /**
     * The value for the Field that user provided
     *
     * @var string
     */
    protected $userValue;

    /**
     * @var \MyProjectBundle\Entity\Field
     */
    protected $field;

    /**
     * @var \MyProjectBundle\Entity\User
     */
    protected $user;

我遇到的问题是$userValue,虽然它在我的数据库中是LONGTEXT,但可以表示实际文本值、日期或数字,具体取决于Field 的类型。

字段可以动态添加。在向任何用户添加 1 后,每个其他用户也可以为该字段填充它自己的值。

在查询数据库时,我使用 orderBy 对某个列进行排序,该列也可以是这些字段之一。在这种情况下,我需要对$userValue 进行排序。当我需要将数字字段排序为数字而不是字符串时,这是有问题的(在这种情况下,'123' 小于 '9'...)。

它的解决方案(我想)是CAST$sort,所以我会得到类似的SQL:

ORDER BY CAST(age AS SIGNED INTEGER) ASC

由于 Doctrine 没有内置的 DQL 函数,我冒昧地将它作为 INT DQL 函数添加到我的项目中(感谢 Jasper N. Brouwer):

class CastAsInteger extends FunctionNode
{
    public $stringPrimary;

    public function getSql(SqlWalker $sqlWalker)
    {
        return 'CAST(' . $this->stringPrimary->dispatch($sqlWalker) . ' AS SIGNED INTEGER)';
    }

    public function parse(Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);

        $this->stringPrimary = $parser->StringPrimary();

        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }
}

很高兴自己找到了一个简单的解决方案,我做到了:

$sort = "INT(".$sort.")";

$queryBuilder->orderBy($sort, $dir);

产生了预期的 DQL:

ORDER BY INT(age) ASC

但也产生了异常:

在渲染模板期间抛出异常(“[Syntax Error] line 0, col 12272: Error: Expected end of string, got '('") in MyProject...

所以我试图找出发生了什么,并在Doctrine\ORM\Query\Parser.php 中了解了这一点:

/**
     * OrderByItem ::= (
     *      SimpleArithmeticExpression | SingleValuedPathExpression |
     *      ScalarExpression | ResultVariable
     * ) ["ASC" | "DESC"]
     *
     * @return \Doctrine\ORM\Query\AST\OrderByItem
     */
    public function OrderByItem()
    {
        ...
    }

这是否意味着无法在ORDER BY 中使用 DQL 函数? 如果是这种情况 - 还有其他方法可以实现吗?

更新

我实际上已经在我的选择查询中使用了 INT,在 CASE WHEN 内:

if ($field->getFieldType() == 'number') {
    $valueThen = "INT(".$valueThen.")";
}

$newFieldAlias = array("
    (CASE
        WHEN ...
        THEN ".$valueThen."
        ELSE ...
        END
    ) as ".$field->getFieldKey());

稍后会将$newFieldAlias 添加到查询中。

不会改变任何东西...

更新 2

即使我在查询中添加了一个额外的选择,也会产生这个 DQL:

SELECT age, INT(age) as int_age

然后像这样排序:

ORDER BY int_age ASC

我仍然没有得到正确的结果。

我从$query->getResult() 中检查了var_dump,这就是我得到的:

'age' => string '57' (length=2)      
'int_age' => string '57' (length=2)

喜欢 CAST 没关系。我一无所知...

【问题讨论】:

  • 我想我解决这个问题的方法是给字段加上别名,然后按别名排序。因此,您可以在选择字段部分尝试 'INT(".$sort.") as SortBy',然后按该别名排序。
  • 是的,对不起,我所有的教义都在起作用,所以我无法检查它,但我确实记得遇到过类似的问题。

标签: php mysql symfony doctrine-orm sql-order-by


【解决方案1】:

Doctrine DQL 不接受函数作为排序标准,但它接受“结果变量”。这意味着您可以执行以下操作:

$q = $this->createQueryBuilder('e')
    ->addSelect('INT(age) as HIDDEN int_age')
    ->orderBy('int_age');

【讨论】:

  • 感谢您的回答,检查更新 2
  • 您仍然以字符串而不是整数的形式获得结果这一事实与原始问题无关。见this thread
【解决方案2】:

Doctrine 2 默认不支持 INT,但可以使用 age+0。

$q = $this->createQueryBuilder('e')
    ->addSelect('age+0 as HIDDEN int_age')
    ->orderBy('int_age');

【讨论】:

    【解决方案3】:

    这是您的 parser.php 文件中的问题。我有类似的问题,我解决了这个问题以替换解析器文件中的以下代码。

    /**
         * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
         *
         * @return \Doctrine\ORM\Query\AST\OrderByClause
         */
        public function OrderByClause()
        {
            $this->match(Lexer::T_ORDER);
            $this->match(Lexer::T_BY);
    
            $orderByItems = array();
            $orderByItems[] = $this->OrderByItem();
    
            while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
                $this->match(Lexer::T_COMMA);
    
                $orderByItems[] = $this->OrderByItem();
            }
    
            return new AST\OrderByClause($orderByItems);
        }
    

    【讨论】:

      【解决方案4】:

      就用这个吧:

      ->orderBy('u.age + 0', 'ASC');
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-25
        相关资源
        最近更新 更多