在Doctrine 2 ORDER BY中使用DQL函数

时间:2014-07-15 18:45:34

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

我正在使用MySQL数据库在Sytrfony 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的类型

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

在查询数据库时,我使用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

但也产生了一个例外:

在渲染模板期间抛出异常(“[语法错误]第0行,第12272行:错误:字符串的预期结束,在MyProject中得到'('”)...

所以我试图找出正在发生的事情并在Doctrine\ORM\Query\Parser.php中加入:{/ p>

/**
     * 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

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

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

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

像CAST一样无所谓。我很无能......

4 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:0)

这是您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);
    }

答案 3 :(得分:-1)

请使用:

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