But if you render a select box via selectMultipleSideBySide to give the plugin a selection of records, the value of this field is stored in a string. This string consists of comma separated UIDs of the selected records in the order they were sorted in the select box (for example "4,2,1,3").
Now you want to have these records read out in exactly this order in the frontend and encounter the problem that there is no suitable query in Extbase to do this.
Fortunately, TYPO3 provides an API for Doctrine DBAL that allows us to use the QueryBuilder to unleash appropriate queries on the database. Sure, you could also split the string into an array using explode and then create a single findByUid query for each contained UID, but performance would suffer.
<?php
declare(strict_types=1);
namespace Vendor\Package\Domain\Repository;
use Vendor\Package\Domain\Model\Item;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
/**
* ItemRepository
*/
class ItemRepository
{
private const TABLE = 'tx_package_domain_model_item';
/**
* @var DataMapper
*/
protected DataMapper $dataMapper;
/**
* @var QueryBuilder|null
*/
protected ?QueryBuilder $queryBuilder = null;
/**
* Constructor
*/
public function __construct(DataMapper $dataMapper)
{
$this->dataMapper = $dataMapper;
$this->queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE);
}
/**
* @param string $uidList
* @return array
*/
public function findByUidList(string $uidList): array
{
$uids = GeneralUtility::intExplode(',', $uidList, true);
if (count($uids) === 0) {
return [];
}
$records = $this->queryBuilder
->select('*')
->from(self::TABLE)
->where($this->queryBuilder->expr()->in('uid', $uids))
->add('orderBy', 'FIELD(uid,' . implode(',', $uids) . ')')
->execute()
->fetchAll();
return $this->dataMapper->map(Item::class, $records);
}
}
In the findByUidList function, a relatively simple query is extended by an orderBy statement in which the UIDs are passed.
Don't be surprised that at the beginning of the function we first take the string apart via explode, but then for the orderBy we combine it into a similar string via implode. With GeneralUtility::intExplode we not only perform an explode, but cast the values occurring there directly to integers, so we can be sure that there are only UIDs in the string. With the third parameter of intExplode even "empty" values are removed directly.