Doctrine's HIDDEN gem
We had a great question from a user recently where the answer involved a
little-known Doctrine feature called HIDDEN
. You won't need it often,
but it deserves its 5 in the spotlight!
HIDDEN
allows you to select a field so that you can sort by it... but without
include it in the results:
/** @return Voyage[] */
public function findAllOrderedByPastFirst(): array
{
return $this->createQueryBuilder('voyage')
->addSelect('CASE WHEN voyage.leaveAt < :now THEN 1 ELSE 0 END AS HIDDEN isPast')
->orderBy('isPast', 'DESC')
->setParameter('now', new \DateTimeImmutable())
->getQuery()
->getResult();
}
The beauty of HIDDEN
is that, to orderBy()
something, that "something" needs
to be selected. But without the HIDDEN
, instead of returning an
array of Voyage
objects, Doctrine would suddenly return an array of arrays,
where each array has a 0
key containing the Voyage
object and an isPast
key.
Yikes!
Here are a few other examples shared by users:
Ordering based on the "state" of an entity thanks to @ker0x:
$this->createQueryBuilder('voyage')
->addSelect(
'CASE
WHEN voyage.state = :confirmed THEN 0
WHEN voyage.state = :canceled THEN 1
WHEN voyage.state = :accepted THEN 2
WHEN voyage.state = :pending THEN 3
ELSE 4
END AS HIDDEN state_order'
)
->setParameters([
'confirmed' => Voyage::STATE_CONFIRMED,
'canceled' => Voyage::STATE_CANCELED,
'accepted' => Voyage::STATE_ACCEPTED,
'pending' => Voyage::STATE_PENDING,
])
->orderBy('state_order', 'ASC');
Ordering null
values first thanks to @Thibault_1635:
$this->createQueryBuilder('voyage')
->addSelect('CASE WHEN voyage.endAt IS NULL THEN 1 ELSE 0 END AS HIDDEN control_null_first')
->orderBy('control_null_first', 'DESC');
Have fun!