Суть в следующем.
Есть таблица с тегами на 11 строк.
Запросом
SELECT `tags`.`id`, `tags`.`name`, `tags`.`slug`, count(`pt`.`tag_id`) as mentions
FROM `tags`
LEFT JOIN `publications_tags` AS `pt`
ON `pt`.`tag_id` = `tags`.`id`
GROUP BY `tags`.`id`
ORDER BY `mentions` DESC
Получаю все строки отсортированные по количеству упоминай в статьях.
При работе непосредственно с базой через heidiSql все запросы возвращают ожидаемый результат.
А вот при получении данных через php запросы возвращают неверные данные - нарушается порядок сортировки.
В частности, запрос вида
SELECT `tags`.`id`, `tags`.`name`, `tags`.`slug`, count(`pt`.`tag_id`) as mentions
FROM `tags`
LEFT JOIN `publications_tags` AS `pt`
ON `pt`.`tag_id` = `tags`.`id`
GROUP BY `tags`.`id`
ORDER BY `mentions` DESC
LIMIT 10 OFFSET 10
Должен вернуть одну строку с id 10 (последняя строка в выборке на скриншоте).
Однако возвращается запись с id 4 (6 строка в выборке).
Для получения записей использую
SelectQuery
из пакета
Cycle\Database\Query
Код моего фетчера.
use Cycle\Database\Injection\Fragment;
use Cycle\Database\Query\SelectQuery;
use Cycle\ORM\ORMInterface;
use Cycle\ORM\SchemaInterface;
use Cycle\ORM\Select\SourceInterface;
abstract class DataFetcher implements OrmAwareInterface
{
/**
* @var Selectable[]
*/
protected array $applies = [];
protected array $columns = [];
protected ?RowFetcher $rowFetcher = null;
/**
* @param Selectable[] $applies
*/
public function __construct(
protected ORMInterface $orm,
array $applies = []
) {
$this->init();
foreach ($applies as $name => $apply) $this->add($name, $apply);
}
public function add(string $name, Selectable $apply): void
{
if ($apply instanceof OrmAwareInterface) $apply->setOrm($this->orm);
$this->applies[$name] = $apply;
}
public function setOrm(ORMInterface $orm): OrmAwareInterface
{
$this->orm = $orm;
return $this;
}
protected function select(SourceInterface $source): SelectQuery
{
return $source->getDatabase()
->select($this->columns)
->from($source->getTable());
}
protected function doFetch(?QueryInterface $query):? Result
{
$source = $this->orm->getSource($this->getRole());
$pk = $this->orm->getSchema()->define($this->getRole(), SchemaInterface::PRIMARY_KEY);
if (is_array($pk)) $pk = "{$source->getTable()}.$pk[0]";
else $pk = "{$source->getTable()}.$pk";
$select = $this->select($source);
$select = $this->apply($query, $select);
if (($count = $this->countRows($select, $pk)) > 0) {
$results = [];
foreach ($select as $row) $results[] = $this->getRowFetcher()->fetch($row);
return new Result($results, $count);
}
return null;
}
protected function getRowFetcher(): RowFetcher
{
if (!$this->rowFetcher) $this->rowFetcher = new RowFetcher($this->getRole(), $this->orm);
return $this->rowFetcher;
}
protected function apply(?QueryInterface $query, SelectQuery $select): SelectQuery
{
if (!$query) return $select;
foreach ($query->toArray() as $name => $value) {
if (isset($this->applies[$name])) {
$select = $this->applies[$name]->apply($select, $value);
if ($this->applies[$name] instanceof RowFetcherInterface) {
$this->getRowFetcher()->extend($this->applies[$name]);
}
}
}
return $select;
}
protected function countRows(SelectQuery $select, string $primaryKey): int
{
// клонирует SelectQuery и выполняет запрос на подсчет строк
return Counter::countDistinct($select, $primaryKey);
}
protected function init(): void
{
$this->initColumns();
$this->add('limit', new ApplyLimit);
$this->add('offset', new ApplyOffset);
}
abstract protected function getRole(): string ;
protected function initColumns(): void
{
if ($this->columns == []) {
$source = $this->orm->getSource($this->getRole());
$columns = $this->orm->getSchema()->define($this->getRole(), SchemaInterface::COLUMNS);
foreach ($columns as $alias => $column) {
if (is_int($alias)) $this->columns[] = "{$source->getTable()}.$column" ;
else $this->columns[] = "{$source->getTable()}.$column as $alias" ;
}
}
}
}
Все данные биндятся правильно. Проверял вплоть до
PDOStatement
драйвера базы данных.
При этом, если сделать параллельный запрос через PDO (новый инстанс).
$pdo = new \PDO('mysql:host=localhost;dbname=dbname', 'user', 'psd');
$stmt = $pdo->prepare('SELECT `tags`.`id`, `tags`.`name`, `tags`.`slug`, count(`pt`.`tag_id`) as mentions
FROM `tags`
LEFT JOIN `publications_tags` AS `pt`
ON `pt`.`tag_id` = `tags`.`id`
GROUP BY `tags`.`id`
ORDER BY `mentions` DESC
LIMIT 10 OFFSET 10');
$stmt->execute();
dd($select->fetchAll(), $stmt->fetchAll(\PDO::FETCH_ASSOC));
То PDO вернет ожидаемый результат, а
$select
описанное поведение.
При этому данное поведение возникает только для пары
limit:10; offset: 10;
Если задать лимит, например, 11 или отступ 9, то работает так, как ожидается.
В чем может быть проблема?