Skip to content

[Collections] Criteria::orderBy() ignores all but first ordering in multi-column sort #437

Open
@tautis154

Description

Bug Report

Q A
Version 2.1.2

Summary

The Criteria::orderBy() method in Doctrine Collections only applies the first ordering pair from the provided array, completely ignoring subsequent orderings. This prevents proper multi-column sorting in Collection filtering.

Current behavior

When using Criteria::orderBy() with multiple sort criteria, only the first key/value pair in the array is applied to the sorting. Any additional orderings are ignored, and changing their direction (ASC/DESC) has no effect on the results.
For example, with two records having the same validFrom date:

When using Criteria::orderBy() with multiple sort criteria, only the first key/value pair in the array is applied to the sorting. Any additional orderings are ignored, and changing their direction (ASC/DESC) has no effect on the results.

For example, with two records having the same validFrom date:

// Records:
// id=5, validFrom='2024-12-10 16:04:11'
// id=6, validFrom='2024-12-10 16:04:11'

$collection->matching(
    Criteria::create()
        ->orderBy([
            'validFrom' => Criteria::DESC,
            'id' => Criteria::DESC  // This is ignored
        ])
);

// Returns id=5 first, despite having same validFrom and lower id

Even changing 'id' => Criteria::ASC has no effect on the results, demonstrating that the second ordering is completely ignored.

Expected behavior

All orderings in the array should be applied in sequence, matching SQL ORDER BY behavior:

  • Primary sorting should be by the first criteria
  • When values are equal for the first ordering, subsequent orderings should be used as tiebreakers
  • In the example above, id=6 should come first when sorting by validFrom DESC, id DESC

How to reproduce

use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\ArrayCollection;

class PriceList
{
    private int $id;
    private \DateTimeImmutable $validFrom;
    
    public function __construct(int $id, \DateTimeImmutable $validFrom) 
    {
        $this->id = $id;
        $this->validFrom = $validFrom;
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function getValidFrom(): \DateTimeImmutable
    {
        return $this->validFrom;
    }
}

// Test setup
$list1 = new PriceList(5, new \DateTimeImmutable('2024-12-10 16:04:11'));
$list2 = new PriceList(6, new \DateTimeImmutable('2024-12-10 16:04:11'));

$collection = new ArrayCollection([$list1, $list2]);

$result = $collection->matching(
    Criteria::create()
        ->orderBy([
            'validFrom' => Criteria::DESC,
            'id' => Criteria::DESC
        ])
        ->setMaxResults(1)
);

// Expected: First result has id=6
// Actual: First result has id=5

// Changing to ASC has no effect, proving second ordering is ignored
$result = $collection->matching(
    Criteria::create()
        ->orderBy([
            'validFrom' => Criteria::DESC,
            'id' => Criteria::ASC
        ])
        ->setMaxResults(1)
);

// Still returns id=5 first

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions