Skip to content

Commit 360bd90

Browse files
committed
Fix yiisoft#20239: fix yii\data\ActiveDataProvider to avoid unexpected pagination results with UNION queries
1 parent b0b7832 commit 360bd90

File tree

4 files changed

+120
-73
lines changed

4 files changed

+120
-73
lines changed

composer.lock

+65-65
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

framework/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Yii Framework 2 Change Log
2727
- Bug #20300: Clear stat cache in `FileCache::setValue()` (rob006)
2828
- Enh #20306: Add new `yii\helpers\ArrayHelper::flatten()` method (xcopy)
2929
- Bug #20308: Allow CompositeAuth auth methods to use their own user if defined (mtangoo)
30+
- Bug #20239: Fix `yii\data\ActiveDataProvider` to avoid unexpected pagination results with UNION queries (Izumi-kun)
3031

3132
2.0.51 July 18, 2024
3233
--------------------

framework/data/ActiveDataProvider.php

+31-8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use yii\base\Model;
1212
use yii\db\ActiveQueryInterface;
1313
use yii\db\Connection;
14+
use yii\db\Query;
1415
use yii\db\QueryInterface;
1516
use yii\di\Instance;
1617

@@ -93,14 +94,40 @@ public function init()
9394
}
9495

9596
/**
96-
* {@inheritdoc}
97+
* Creates a wrapper of [[query]] that allows adding limit and order.
98+
* @return QueryInterface
99+
* @throws InvalidConfigException
97100
*/
98-
protected function prepareModels()
101+
protected function createQueryWrapper(): QueryInterface
99102
{
100103
if (!$this->query instanceof QueryInterface) {
101104
throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
102105
}
103-
$query = clone $this->query;
106+
$wrapper = clone $this->query;
107+
if ($wrapper instanceof Query && !empty($wrapper->union)) {
108+
$wrapper->where = [];
109+
$wrapper->limit = null;
110+
$wrapper->offset = null;
111+
$wrapper->orderBy = [];
112+
$wrapper->selectOption = null;
113+
$wrapper->distinct = false;
114+
$wrapper->groupBy = [];
115+
$wrapper->join = [];
116+
$wrapper->having = [];
117+
$wrapper->union = [];
118+
$wrapper->params = [];
119+
$wrapper->withQueries = [];
120+
$wrapper->select('*')->from(['q' => $this->query]);
121+
}
122+
return $wrapper;
123+
}
124+
125+
/**
126+
* {@inheritdoc}
127+
*/
128+
protected function prepareModels()
129+
{
130+
$query = $this->createQueryWrapper();
104131
if (($pagination = $this->getPagination()) !== false) {
105132
$pagination->totalCount = $this->getTotalCount();
106133
if ($pagination->totalCount === 0) {
@@ -161,11 +188,7 @@ protected function prepareKeys($models)
161188
*/
162189
protected function prepareTotalCount()
163190
{
164-
if (!$this->query instanceof QueryInterface) {
165-
throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
166-
}
167-
$query = clone $this->query;
168-
return (int) $query->limit(-1)->offset(-1)->orderBy([])->count('*', $this->db);
191+
return (int) $this->createQueryWrapper()->limit(-1)->offset(-1)->orderBy([])->count('*', $this->db);
169192
}
170193

171194
/**

0 commit comments

Comments
 (0)