如何使用注解在 Doctrine 2 集合上首先排序 NULL 值?

How can I order NULL values first on a Doctrine 2 collection using annotations?

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

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

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

当前方法的问题在于它将结束日期为 NULL 的事件排在所有其他事件之后。

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

到目前为止,我已经在 SO 上看到了几个关于如何告诉 Doctrine 如何对实体排序的问题。但是,none 其中提到了注释。按照建议反转符号的技巧,例如在 Doctrine 2 Order By ASC and Null values in last 中不起作用,因为 Doctrine 不接受注释中 属性 名称和 ASCDESC 以外的任何内容。

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

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

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

    // 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 值,您只需将第一个 'if' 更改为相反的符号即可。

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

我的解决方法是在查询中添加一个额外的 select 并从生成的数组集合中提取实体,最好只在查询时检查它而不是 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());

这是一个旧的 post,但如果您使用的是学说查询生成器,我找到了一个非常简单的解决方案:

$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 方式,除了更慢之外,避免使用偏移量(例如无限滚动)

感谢