From e691713ed7539688e93492d86fb725fa044c091f Mon Sep 17 00:00:00 2001 From: rhertogh Date: Sun, 23 Jan 2022 21:26:59 +0100 Subject: [PATCH] Fix #19171: Added `$pagination` and `$sort` to `\yii\rest\IndexAction` for easy configuration --- docs/guide/rest-quick-start.md | 36 ++++++++++ framework/CHANGELOG.md | 1 + framework/rest/IndexAction.php | 57 +++++++++++++-- tests/framework/rest/IndexActionTest.php | 120 +++++++++++++++++++++++++++++++ 4 files changed, 208 insertions(+), 6 deletions(-) diff --git a/docs/guide/rest-quick-start.md b/docs/guide/rest-quick-start.md index e04121c..d1cc8ad 100644 --- a/docs/guide/rest-quick-start.md +++ b/docs/guide/rest-quick-start.md @@ -195,6 +195,42 @@ Additionally, you can sort collections like `http://localhost/users?sort=email` `http://localhost/users?filter[email][like]=gmail.com` could be implemented using data filters. See [Resources](rest-resources.md#filtering-collections) section for details. +## Customizing Pagination and Sorting in the list + +In order to change the default [pagination](output-pagination.md) and [sorting](output-sorting.md) of the model list +you can configure the [[yii\rest\IndexAction]] in your controller. For example: + +```php + [ + 'pagination' => [ + 'pageSize' => 10, + ], + 'sort' => [ + 'defaultOrder' => [ + 'created_at' => SORT_DESC, + ], + ], + ], + ]); + } +} +``` + +Please see [Extending ActiveController](rest-controllers#extending-active-controller) for more information on how to +configure actions of the ActiveController. ## Summary diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index ba60ef4..dc7f5f5 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -8,6 +8,7 @@ Yii Framework 2 Change Log - Bug #19138: Allow digits in language code (ntesic) - Bug #19148: Fix undefined array key errors in `yii\db\ActiveRelationTrait` (stevekr) - Bug #19041: Fix PHP 8.1 issues (longthanhtran, samdark, pamparam83, sartor, githubjeka) +- Enh #19171: Added `$pagination` and `$sort` to `\yii\rest\IndexAction` for easy configuration (rhertogh) 2.0.44 December 30, 2021 diff --git a/framework/rest/IndexAction.php b/framework/rest/IndexAction.php index 6ced938..25fae9a 100644 --- a/framework/rest/IndexAction.php +++ b/framework/rest/IndexAction.php @@ -10,6 +10,9 @@ namespace yii\rest; use Yii; use yii\data\ActiveDataProvider; use yii\data\DataFilter; +use yii\data\Pagination; +use yii\data\Sort; +use yii\helpers\ArrayHelper; /** * IndexAction implements the API endpoint for listing multiple models. @@ -87,6 +90,24 @@ class IndexAction extends Action /** + * @var array|Pagination|false The pagination to be used by [[prepareDataProvider()]]. + * If this is `false`, it means pagination is disabled. + * Note: if a Pagination object is passed, it's `params` will be set to the request parameters. + * @see Pagination + * @since 2.0.45 + */ + public $pagination = []; + + /** + * @var array|Sort|false The sorting to be used by [[prepareDataProvider()]]. + * If this is `false`, it means sorting is disabled. + * Note: if a Sort object is passed, it's `params` will be set to the request parameters. + * @see Sort + * @since 2.0.45 + */ + public $sort = []; + + /** * @return ActiveDataProvider */ public function run() @@ -135,15 +156,39 @@ class IndexAction extends Action $query = call_user_func($this->prepareSearchQuery, $query, $requestParams); } + if (is_array($this->pagination)) { + $pagination = ArrayHelper::merge( + [ + 'params' => $requestParams, + ], + $this->pagination + ); + } else { + $pagination = $this->pagination; + if ($this->pagination instanceof Pagination) { + $pagination->params = $requestParams; + } + } + + if (is_array($this->sort)) { + $sort = ArrayHelper::merge( + [ + 'params' => $requestParams, + ], + $this->sort + ); + } else { + $sort = $this->sort; + if ($this->sort instanceof Sort) { + $sort->params = $requestParams; + } + } + return Yii::createObject([ 'class' => ActiveDataProvider::className(), 'query' => $query, - 'pagination' => [ - 'params' => $requestParams, - ], - 'sort' => [ - 'params' => $requestParams, - ], + 'pagination' => $pagination, + 'sort' => $sort, ]); } } diff --git a/tests/framework/rest/IndexActionTest.php b/tests/framework/rest/IndexActionTest.php index 385749c..ffbf6e9 100644 --- a/tests/framework/rest/IndexActionTest.php +++ b/tests/framework/rest/IndexActionTest.php @@ -3,6 +3,9 @@ namespace yiiunit\framework\rest; use Yii; +use yii\data\ActiveDataProvider; +use yii\data\Pagination; +use yii\data\Sort; use yii\db\ActiveRecord; use yii\db\Query; use yii\rest\ActiveController; @@ -63,6 +66,123 @@ class IndexActionTest extends TestCase $sql ); } + + /** + * @dataProvider dataProviderTestPrepareDataProviderWithPaginationAndSorting + * + * @param string $sql + * @param array $params + * @param string $expectedRawSql + */ + public function testPrepareDataProviderWithPaginationAndSorting( + $pagination, + $sort, + $expectedPaginationPageSize = null, + $expectedPaginationDefaultPageSize = null, + $expectedSortOrders = [], + $expectedSortDefaultOrder = null + ) { + Yii::$app->getRequest()->setBodyParams([ + 'per-page' => 11, + 'sort' => '-test-sort' + ]); + + $controller = new RestController( + 'rest', + new Module('rest'), [ + 'modelClass' => IndexActionModel::className(), + 'actions' => [ + 'index' => [ + 'class' => IndexAction::className(), + 'modelClass' => IndexActionModel::className(), + 'pagination' => $pagination, + 'sort' => $sort, + ], + ], + ]); + + /** @var ActiveDataProvider $dataProvider */ + $dataProvider = $controller->createAction('index')->runWithParams([]); + $actualPagination = $dataProvider->getPagination(); + $actualSort = $dataProvider->getSort(); + + if ($pagination === false) { + $this->assertFalse($actualPagination); + } else { + $this->assertEquals($expectedPaginationPageSize, $actualPagination->pageSize); + $this->assertEquals($expectedPaginationDefaultPageSize, $actualPagination->defaultPageSize); + } + + if ($sort === false) { + $this->assertFalse($actualSort); + } else { + $this->assertEquals($expectedSortOrders, $actualSort->getOrders()); + $this->assertEquals($expectedSortDefaultOrder, $actualSort->defaultOrder); + } + } + + /** + * Data provider for [[testPrepareDataProviderWithPaginationAndSorting()]]. + * @return array test data + */ + public function dataProviderTestPrepareDataProviderWithPaginationAndSorting() + { + return [ + [ // Default config + [], + [], + 11, // page size set as param in test + (new Pagination())->defaultPageSize, + [], + null + ], + [ // Default config + [], + [ + 'attributes' => ['test-sort'], + ], + 11, // page size set as param in test + (new Pagination())->defaultPageSize, + ['test-sort' => SORT_DESC], // test sort set as param in test + null + ], + [ // Config via array + [ + 'pageSize' => 12, // Testing a fixed page size + 'defaultPageSize' => 991, + ], + [ + 'attributes' => ['test-sort'], + 'defaultOrder' => [ + 'created_at_1' => SORT_DESC, + ], + ], + 12, + 991, + ['test-sort' => SORT_DESC], // test sort set as param in test + ['created_at_1' => SORT_DESC] + ], + [ // Config via objects + new Pagination([ + 'defaultPageSize' => 992, + ]), + new Sort([ + 'attributes' => ['created_at_2'], + 'defaultOrder' => [ + 'created_at_2' => SORT_DESC, + ], + ]), + 11, // page size set as param in test + 992, + ['created_at_2' => SORT_DESC], // test sort set as param in test is ignored + ['created_at_2' => SORT_DESC] + ], + [ // Disable pagination and sort + false, + false, + ] + ]; + } } class RestController extends ActiveController