如何使用注释首先在Doctrine 2集合上订购NULL值?

时间:2015-03-04 10:42:54

标签: php symfony sorting doctrine-orm

我有一个使用Symfony 2并包含Doctrine 2实体的项目。其中一些实体彼此相关。此关联由注释定义:

/**
 * @ORM\OneToMany(targetEntity="Event", mappedBy="firstEntityId" cascade={"persist", "remove"})
 * @ORM\OrderBy({"dateEnd" = "DESC", "dateBegin" = "DESC"})
*/
private $events;

如您所见,此关联包含多个具有开始日期和结束日期的事件。在检索此集合时,我希望最多的事件(即那些尚未结束或最近结束的事件)先排序。

当前方法的问题在于,它会在所有其他事件之后对结束日期为NULL的事件进行排序。

如何告诉Doctrine首先对结束日期为NULL的事件进行排序,然后按结束日期降序对其余事件进行排序?

到目前为止,我已经看到了几个关于如何告诉Doctrine如何订购实体的问题。但是,他们都没有提到注释。如建议的那样使用反转符号的技巧,例如在Doctrine 2 Order By ASC and Null values in last中不起作用,因为Doctrine在注释中不接受除属性名称和ASCDESC以外的任何内容。

4 个答案:

答案 0 :(得分:7)

这是一篇旧帖子,但如果您使用的是doctrine查询构建器,我发现了一个非常简单的解决方案:

$sortDirection = 'ASC';
$qb = $this->createQueryBuilder('e');
$qb->addSelect('CASE WHEN e.valueToOrder IS NULL THEN 1 ELSE 0 END AS HIDDEN myValueIsNull');

//other stuffs
//$qb->where ...

$qb->orderBy('myValueIsNull','ASC');
$qb->addOrderBy('e.valueToOrder',':sortDirection');
$qb->setParameter(':sortDirection',$sortDirection);

return $qb->getQuery()->getResult();

PHP方式,除了慢一点,避免使用偏移(例如无限滚动)

感谢https://stackoverflow.com/a/23420682/6376420

答案 1 :(得分:3)

可能不是。有一种允许ORDER BY column DESC NULLS FIRST的SQL语法。但是,并非所有数据库供应商都支持它,因此如果我正确扫描了合并请求,则不是merged into DQL。根据您使用的数据库平台,您可能会很幸运。合并请求中的注释提供了如何在不同点扩展Doctrine以实现行为的洞察力,也许这可以帮助您自己完成。

答案 2 :(得分:1)

我遇到了同样的问题,这是我的方法:

如果我们不是在谈论大量处理,您可以使用自定义排序,我需要根据用户选择将结果按asc或desc中的列排序。但是,我还需要首先出现相同列的空值。因此,经过大量谷歌搜索NULLS FIRST方法后,我决定在从查询构建器中获得结果后立即进行操作:

    // Custom sort to put the nulls first
    usort($invoices, function(Entity $a, Entity $b) use ($order) {
        if(null === $a->getNumber())
            return -1;
        if(null === $b->getNumber())
            return 1;

        if(strtoupper($order) == "DESC") {
            if($a->getNumber() > $b->getNumber())
                return -1;
            if($b->getNumber() > $a->getNumber())
                return 1;
        } else {
            if($a->getNumber() < $b->getNumber())
                return -1;
            if($b->getNumber() < $a->getNumber())
                return 1;
        }
    });

这样,当您从QueryBuilder获得结果时,您将首先获得NULLS,然后您将获得原始排序。如果它是ASC,它将保持ASC,反之亦然。

如果最后需要NULL值,您只需更改第一个&#39; if&#39;相反的迹象。

我知道这个问题已经回答了,但我想我可以把它放在这里,以防它帮助其他人。

答案 3 :(得分:1)

我的解决方法是创建为查询添加一个额外的select并从结果数组中提取实体,最好只在查询时检查它而不选择它(保持结果数组完整)但我还没有找到适当的解决方案(使用QueryBuilder)。

$queryBuilder = $this->getEntityManager()->createQueryBuilder();
$queryBuilder->select('e')
    ->from(Entity::class, 'e')
    // We use ZZZZ here as placeholder to push the null values last, use 'AAAA' to sort them first.
    ->addSelect('CASE WHEN(e.name IS NULL) THEN \'ZZZZ\' ELSE e.name END AS name')
    ->addOrderBy('name', 'ASC');

    // As we have a array result due to the "addSelect()" above, we must extract the entities now, in this example by looping over the result array.
    $entities = array_map(function ($contributor) {
        return $contributor[0];
    }, $queryBuilder->getQuery()->getResult());