TYPO3 Doctrine QueryBuilder und COUNT(field) im SELECT Query

Wir wollen folgendes SQL Query mit TYPO3 QueryBuilder erstellen:

SELECT uid, title, COUNT(uid) AS count
FROM  fe_groups
WHERE uid NOT IN (351,225,352,353,359,1013,686)
GROUP BY title
HAVING count > 1
ORDER BY count DESC;

Zuerst erstellen wir eine neue Instanz von QueryBuilder (dem von TYPO3, nicht dem von Doctrine)

/** @var \TYPO3\CMS\Core\Database\Query\QueryBuilder $queryBuilder */
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('fe_groups');

Danach wäre der erste Versuch wie folgt gewesen (funktioniert auch lt. Doctrine Dokumentation):

 $queryBuilder->select('fe_groups.uid, COUNT(fe_groups.uid) AS count')

oder als zweiter Versuch auch:

 $queryBuilder->select('fe_groups.uid', 'COUNT(fe_groups.uid) AS count')

Funktioniert leider beides nicht mit dem TYPO3 QueryBuilder (der ein Wrapper auf den Doctrine QueryBuilder darstellt),
da dieser in der select Methode noch alle Parameter durch quoteIdentifiersForSelect jagt und mit dem SQL Statement etwas wie

An exception occurred while executing 'SELECT `fe_groups`.`uid, COUNT(fe_groups`.`uid)` AS `count` FROM `fe_groups`

herauskommen würde.

In der Dokumentation zum ExpressionBuilder  wird man dann fündig und stößt auf eine Methode namens addSelectLiteral mit der es möglich ist alle diese s.g. Aggregatfunktionen umzusetzen. Dazu holen wir uns vom QueryBuilder die Instanz vom ExpressionBuilder (mit der expr Methode) und rufen dort die gewünschte Aggregatfunktion (in unserem Fall count mit dem gewünschten Feldnamen und als zweitem Parameter einem optionalen Alias (aka AS)) auf.

$queryBuilder->select('fe_groups.uid')
 ->addSelectLiteral(
   $queryBuilder->expr()->count('fe_groups.uid', 'count')
 )
 ->from('fe_groups')
 ->where('fe_groups.uid NOT IN (' . $queryBuilder->quote($excludeGroupIdList) . ')')
 ->groupBy('fe_groups.title')
 ->having('count > 1')
 ->orderBy('count', 'desc');

Damit erhalten wir das oben angeführte SQL Statement, mit dessen Ergebnis wir dann wie gewohnt weiterarbeiten können.

/** @var \Doctrine\DBAL\Driver\Statement $result */
$result = $queryBuilder->execute();

while ($row = $result->fetch()) {
   doSomething($row);
}