From 3fa2d61e5436321b7499d55bb3206c83ba07e43a Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 8 Jun 2024 10:29:40 +0300 Subject: [PATCH] Fix #20175: Fix bad result for pagination when used with GridView --- build/controllers/TranslationController.php | 1 - framework/CHANGELOG.md | 2 +- framework/base/ErrorException.php | 2 - framework/base/Security.php | 1 - framework/data/ActiveDataProvider.php | 16 ++++--- framework/data/BaseDataProvider.php | 10 ++-- framework/data/Pagination.php | 46 ++++++++++++------- framework/data/SqlDataProvider.php | 3 -- framework/db/ColumnSchemaBuilder.php | 1 - framework/i18n/DbMessageSource.php | 1 - framework/log/DbTarget.php | 1 - framework/log/Logger.php | 1 - framework/log/SyslogTarget.php | 1 - framework/rbac/Assignment.php | 1 - framework/rest/Action.php | 1 - framework/rest/ViewAction.php | 2 - framework/web/CacheSession.php | 1 - framework/web/HeaderCollection.php | 1 - .../framework/data/ActiveDataProviderTest.php | 19 ++++++++ 19 files changed, 64 insertions(+), 47 deletions(-) diff --git a/build/controllers/TranslationController.php b/build/controllers/TranslationController.php index 3ca61667620..550b74975db 100644 --- a/build/controllers/TranslationController.php +++ b/build/controllers/TranslationController.php @@ -8,7 +8,6 @@ namespace yii\build\controllers; use DirectoryIterator; -use Yii; use yii\console\Controller; use yii\helpers\Html; diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 8f2209d17ff..09d3066e7b5 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,7 +4,7 @@ Yii Framework 2 Change Log 2.0.51 under development ------------------------ -- no changes in this release. +- Bug #20175: Fix bad result for pagination when used with GridView (@lav45) 2.0.50 May 30, 2024 diff --git a/framework/base/ErrorException.php b/framework/base/ErrorException.php index 8f051a57e1c..f9c388a5453 100644 --- a/framework/base/ErrorException.php +++ b/framework/base/ErrorException.php @@ -7,8 +7,6 @@ namespace yii\base; -use Yii; - /** * ErrorException represents a PHP error. * diff --git a/framework/base/Security.php b/framework/base/Security.php index c05084654c0..85faf50e59f 100644 --- a/framework/base/Security.php +++ b/framework/base/Security.php @@ -7,7 +7,6 @@ namespace yii\base; -use Yii; use yii\helpers\StringHelper; /** diff --git a/framework/data/ActiveDataProvider.php b/framework/data/ActiveDataProvider.php index 6343e320473..357b1eb74ef 100644 --- a/framework/data/ActiveDataProvider.php +++ b/framework/data/ActiveDataProvider.php @@ -110,7 +110,6 @@ protected function prepareModels() if (($sort = $this->getSort()) !== false) { $query->addOrderBy($sort->getOrders()); } - return $query->all($this->db); } @@ -128,7 +127,6 @@ protected function prepareKeys($models) $keys[] = call_user_func($this->key, $model); } } - return $keys; } elseif ($this->query instanceof ActiveQueryInterface) { /* @var $class \yii\db\ActiveRecordInterface */ @@ -148,13 +146,13 @@ protected function prepareKeys($models) $keys[] = $kk; } } - return $keys; } - return array_keys($models); } + private $_totalCount = []; + /** * {@inheritdoc} */ @@ -163,8 +161,13 @@ protected function prepareTotalCount() if (!$this->query instanceof QueryInterface) { 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.'); } - $query = clone $this->query; - return (int) $query->limit(-1)->offset(-1)->orderBy([])->count('*', $this->db); + $query = (clone $this->query)->limit(-1)->offset(-1)->orderBy([]); + $key = md5((string)$query); + + if (!array_key_exists($key, $this->_totalCount)) { + $this->_totalCount[$key] = (int)$query->count('*', $this->db); + } + return $this->_totalCount[$key]; } /** @@ -196,7 +199,6 @@ public function __clone() if (is_object($this->query)) { $this->query = clone $this->query; } - parent::__clone(); } } diff --git a/framework/data/BaseDataProvider.php b/framework/data/BaseDataProvider.php index 70c37bd6578..22fa1a0ad4f 100644 --- a/framework/data/BaseDataProvider.php +++ b/framework/data/BaseDataProvider.php @@ -167,10 +167,10 @@ public function getTotalCount() if ($this->_pagination === false) { return $this->getCount(); } - if ($this->_totalCount === null) { - $this->_totalCount = $this->prepareTotalCount(); + if ($this->_totalCount !== null) { + return (int)$this->_totalCount; } - return $this->_totalCount; + return $this->prepareTotalCount(); } /** @@ -219,7 +219,9 @@ public function setPagination($value) $value = Yii::createObject(array_merge($config, $value)); } if ($value instanceof Pagination) { - $value->totalCount = $this->getTotalCount(); + $value->setTotalCount(function () { + return $this->getTotalCount(); + }); $this->_pagination = $value; } elseif ($value === false) { $this->_pagination = false; diff --git a/framework/data/Pagination.php b/framework/data/Pagination.php index a928681c4da..be6cc812d27 100644 --- a/framework/data/Pagination.php +++ b/framework/data/Pagination.php @@ -7,6 +7,7 @@ namespace yii\data; +use Closure; use Yii; use yii\base\BaseObject; use yii\web\Link; @@ -69,6 +70,7 @@ * @property-read int $pageCount Number of pages. * @property int $pageSize The number of items per page. If it is less than 1, it means the page size is * infinite, and thus a single page contains all items. + * @property int $totalCount total number of items. * * @author Qiang Xue * @since 2.0 @@ -123,10 +125,6 @@ class Pagination extends BaseObject implements Linkable * number validation. By doing so, [[page]] will return the value indexed by [[pageParam]] in [[params]]. */ public $validatePage = true; - /** - * @var int total number of items. - */ - public $totalCount = 0; /** * @var int the default page size. This property will be returned by [[pageSize]] when page size * cannot be determined by [[pageSizeParam]] from [[params]]. @@ -143,6 +141,10 @@ class Pagination extends BaseObject implements Linkable * If it is less than 1, it means the page size is infinite, and thus a single page contains all items. */ private $_pageSize; + /** + * @var Closure|int total number of items or closure returning it. + */ + private $_totalCount = 0; /** @@ -151,13 +153,11 @@ class Pagination extends BaseObject implements Linkable public function getPageCount() { $pageSize = $this->getPageSize(); + $totalCount = $this->getTotalCount(); if ($pageSize < 1) { - return $this->totalCount > 0 ? 1 : 0; + return $totalCount > 0 ? 1 : 0; } - - $totalCount = $this->totalCount < 0 ? 0 : (int) $this->totalCount; - - return (int) (($totalCount + $pageSize - 1) / $pageSize); + return (int) ((max($totalCount, 0) + $pageSize - 1) / $pageSize); } private $_page; @@ -173,7 +173,6 @@ public function getPage($recalculate = false) $page = (int) $this->getQueryParam($this->pageParam, 1) - 1; $this->setPage($page, true); } - return $this->_page; } @@ -221,7 +220,6 @@ public function getPageSize() $this->setPageSize($pageSize, true); } } - return $this->_pageSize; } @@ -264,7 +262,7 @@ public function createUrl($page, $pageSize = null, $absolute = false) $request = Yii::$app->getRequest(); $params = $request instanceof Request ? $request->getQueryParams() : []; } - if ($page > 0 || $page == 0 && $this->forcePageParam) { + if ($page > 0 || ($page === 0 && $this->forcePageParam)) { $params[$this->pageParam] = $page + 1; } else { unset($params[$this->pageParam]); @@ -282,7 +280,6 @@ public function createUrl($page, $pageSize = null, $absolute = false) if ($absolute) { return $urlManager->createAbsoluteUrl($params); } - return $urlManager->createUrl($params); } @@ -293,7 +290,6 @@ public function createUrl($page, $pageSize = null, $absolute = false) public function getOffset() { $pageSize = $this->getPageSize(); - return $pageSize < 1 ? 0 : $this->getPage() * $pageSize; } @@ -305,7 +301,6 @@ public function getOffset() public function getLimit() { $pageSize = $this->getPageSize(); - return $pageSize < 1 ? -1 : $pageSize; } @@ -331,7 +326,6 @@ public function getLinks($absolute = false) $links[self::LINK_NEXT] = $this->createUrl($currentPage + 1, null, $absolute); } } - return $links; } @@ -348,7 +342,25 @@ protected function getQueryParam($name, $defaultValue = null) $request = Yii::$app->getRequest(); $params = $request instanceof Request ? $request->getQueryParams() : []; } - return isset($params[$name]) && is_scalar($params[$name]) ? $params[$name] : $defaultValue; } + + /** + * @return int total number of items. + */ + public function getTotalCount() + { + if (is_numeric($this->_totalCount)) { + return (int)$this->_totalCount; + } + return (int)call_user_func($this->_totalCount); + } + + /** + * @param Closure|int $count + */ + public function setTotalCount($count) + { + $this->_totalCount = $count; + } } diff --git a/framework/data/SqlDataProvider.php b/framework/data/SqlDataProvider.php index 12a1d2c0c78..c173a23d973 100644 --- a/framework/data/SqlDataProvider.php +++ b/framework/data/SqlDataProvider.php @@ -7,7 +7,6 @@ namespace yii\data; -use Yii; use yii\base\InvalidConfigException; use yii\db\Connection; use yii\db\Expression; @@ -150,10 +149,8 @@ protected function prepareKeys($models) $keys[] = call_user_func($this->key, $model); } } - return $keys; } - return array_keys($models); } diff --git a/framework/db/ColumnSchemaBuilder.php b/framework/db/ColumnSchemaBuilder.php index 756b4b8aa8b..02ca9f2fee9 100644 --- a/framework/db/ColumnSchemaBuilder.php +++ b/framework/db/ColumnSchemaBuilder.php @@ -7,7 +7,6 @@ namespace yii\db; -use Yii; use yii\base\BaseObject; use yii\helpers\StringHelper; diff --git a/framework/i18n/DbMessageSource.php b/framework/i18n/DbMessageSource.php index 0686291d2a7..e7934be3564 100644 --- a/framework/i18n/DbMessageSource.php +++ b/framework/i18n/DbMessageSource.php @@ -7,7 +7,6 @@ namespace yii\i18n; -use Yii; use yii\base\InvalidConfigException; use yii\caching\CacheInterface; use yii\db\Connection; diff --git a/framework/log/DbTarget.php b/framework/log/DbTarget.php index 398c49c9fed..d32c292c4f3 100644 --- a/framework/log/DbTarget.php +++ b/framework/log/DbTarget.php @@ -7,7 +7,6 @@ namespace yii\log; -use Yii; use yii\base\InvalidConfigException; use yii\db\Connection; use yii\db\Exception; diff --git a/framework/log/Logger.php b/framework/log/Logger.php index 6be7eaf2a92..62294c31f93 100644 --- a/framework/log/Logger.php +++ b/framework/log/Logger.php @@ -7,7 +7,6 @@ namespace yii\log; -use Yii; use yii\base\Component; /** diff --git a/framework/log/SyslogTarget.php b/framework/log/SyslogTarget.php index 8a190d4fb5a..4c22a8b4fe7 100644 --- a/framework/log/SyslogTarget.php +++ b/framework/log/SyslogTarget.php @@ -7,7 +7,6 @@ namespace yii\log; -use Yii; use yii\helpers\VarDumper; /** diff --git a/framework/rbac/Assignment.php b/framework/rbac/Assignment.php index 21e2ad03bcd..db299813eec 100644 --- a/framework/rbac/Assignment.php +++ b/framework/rbac/Assignment.php @@ -7,7 +7,6 @@ namespace yii\rbac; -use Yii; use yii\base\BaseObject; /** diff --git a/framework/rest/Action.php b/framework/rest/Action.php index c700d665c8f..49cc4236f66 100644 --- a/framework/rest/Action.php +++ b/framework/rest/Action.php @@ -7,7 +7,6 @@ namespace yii\rest; -use Yii; use yii\base\InvalidConfigException; use yii\db\ActiveRecordInterface; use yii\web\NotFoundHttpException; diff --git a/framework/rest/ViewAction.php b/framework/rest/ViewAction.php index dbe9bb2ac92..901fa6beece 100644 --- a/framework/rest/ViewAction.php +++ b/framework/rest/ViewAction.php @@ -7,8 +7,6 @@ namespace yii\rest; -use Yii; - /** * ViewAction implements the API endpoint for returning the detailed information about a model. * diff --git a/framework/web/CacheSession.php b/framework/web/CacheSession.php index 860a91d5803..5763a854096 100644 --- a/framework/web/CacheSession.php +++ b/framework/web/CacheSession.php @@ -7,7 +7,6 @@ namespace yii\web; -use Yii; use yii\caching\CacheInterface; use yii\di\Instance; diff --git a/framework/web/HeaderCollection.php b/framework/web/HeaderCollection.php index 724e875bc69..17eaadefd6d 100644 --- a/framework/web/HeaderCollection.php +++ b/framework/web/HeaderCollection.php @@ -7,7 +7,6 @@ namespace yii\web; -use Yii; use yii\base\BaseObject; /** diff --git a/tests/framework/data/ActiveDataProviderTest.php b/tests/framework/data/ActiveDataProviderTest.php index 789a0337260..da3e70df945 100644 --- a/tests/framework/data/ActiveDataProviderTest.php +++ b/tests/framework/data/ActiveDataProviderTest.php @@ -197,4 +197,23 @@ public function testDoesNotPerformQueryWhenHasNoModels() $this->assertEquals(0, $pagination->getPageCount()); } + + public function testTotalCountAfterSearch() + { + $query = Order::find(); + $provider = new ActiveDataProvider([ + 'query' => $query, + 'pagination' => [ + 'pageSize' => 2, + ], + ]); + + $pagination = $provider->getPagination(); + $this->assertEquals(2, $pagination->getPageCount()); + $this->assertEquals(3, $pagination->getTotalCount()); + + $query->andWhere(['customer_id' => 2]); + $this->assertEquals(1, $pagination->getPageCount()); + $this->assertEquals(2, $pagination->getTotalCount()); + } }