Photo of Torben Hansen

A TechBlog by Torben Hansen


Freelance Full Stack Web Developer located in Germany.
I create web applications mainly using TYPO3, PHP, Python and JavaScript.
Home Archive Tags

Sorting by UIDs with MySQL FIELD function in TYPO3 13.4+ using Doctrine DBAL 4

Back in 2017, I wrote this blogpost about how to use the MySQL FIELD function in TYPO3 with Doctrine DBAL. The function is used to apply a special ordering of records by a given list of uids (e.g. from FlexForm) to a query using QueryBuilder and then uses the extbase datamapper to transform the raw Doctrine query result to an array of domain model objects.

Unfortunately, the solution does not work anymore in TYPO3 13.4+, since the $queryBuilder->add() function has been removed. To the rescue, it is still possible to apply the special ordering to the query using the FIELD function, but in a different way.

Instead of using the FIELD function directly in the ORDER BY query part, it is now used as select literal to add the current order index as a new column to the query result. The new column is then used in the query via $queryBuilder->orderBy() as shown below.

<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use Doctrine\DBAL\ArrayParameterType;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;
use TYPO3\CMS\Extbase\Persistence\Repository;
use MyVendor\MyExtension\Domain\Model\MyModel;

class MyModelRepository extends Repository
{
    public function findByUidList(string $uidList = '')
    {
        $uids = GeneralUtility::intExplode(',', $uidList, true);
        if ($uids === []) {
            return [];
        }

        $dataMapper = GeneralUtility::makeInstance(DataMapper::class);
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
            ->getQueryBuilderForTable('tx_myext_domain_model_mymodel');

        $rows = $queryBuilder
            ->select('*')
            ->addSelectLiteral(
                sprintf('FIELD(tx_myext_domain_model_mymodel.uid, %s) AS custom_ordering', implode(',', $uids))
            )
            ->from('tx_myext_domain_model_mymodel')
            ->where(
                $queryBuilder->expr()->in(
                    'uid',
                    $queryBuilder->createNamedParameter(
                        $uids,
                        ArrayParameterType::INTEGER
                    )
                )
            )
            ->orderBy('custom_ordering')
            ->executeQuery()
            ->fetchAllAssociative();

        return $dataMapper->map(MyModel::class, $rows);
    }
}

The important part here is addSelectLiteral, where the MySQL FIELD function is used to add the new column custom_ordering containing the ordering index.

Note, that the additional column custom_ordering is not taken into account when the result is processed by DataMapper, because DataMapper only processes columns, which are accessible properties of the domain model.