Skip to content

Commit b0ec150

Browse files
Code-Workingmarkstory
authored andcommitted
Paginator now supports ordering by multiple fields.
1 parent 5564feb commit b0ec150

File tree

2 files changed

+71
-3
lines changed

2 files changed

+71
-3
lines changed

src/Datasource/Paginator.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ public function paginate($object, array $params = [], array $settings = [])
215215
'sortDefault' => $sortDefault,
216216
'directionDefault' => $directionDefault,
217217
'scope' => $options['scope'],
218+
'completeSort' => $order,
218219
];
219220

220221
$this->_pagingParams = [$alias => $paging];
@@ -323,7 +324,7 @@ public function getDefaults($alias, $settings)
323324
* the model friendly order key.
324325
*
325326
* You can use the whitelist parameter to control which columns/fields are
326-
* available for sorting. This helps prevent users from ordering large
327+
* available for sorting via URL parameters. This helps prevent users from ordering large
327328
* result sets on un-indexed values.
328329
*
329330
* If you need to sort on associated columns or synthetic properties you
@@ -333,6 +334,9 @@ public function getDefaults($alias, $settings)
333334
* You can use this to sort on synthetic columns, or columns added in custom
334335
* find operations that may not exist in the schema.
335336
*
337+
* The default order options provided to paginate() will be merged with the user's
338+
* requested sorting field/direction.
339+
*
336340
* @param \Cake\Datasource\RepositoryInterface $object Repository object.
337341
* @param array $options The pagination options being used for this request.
338342
* @return array An array of options with sort + direction removed and
@@ -348,7 +352,8 @@ public function validateSort(RepositoryInterface $object, array $options)
348352
if (!in_array($direction, ['asc', 'desc'])) {
349353
$direction = 'asc';
350354
}
351-
$options['order'] = [$options['sort'] => $direction];
355+
$order = (isset($options['order']) && is_array($options['order'])) ? $options['order'] : [];
356+
$options['order'] = [$options['sort'] => $direction] + $order;
352357
}
353358
unset($options['sort'], $options['direction']);
354359

@@ -369,7 +374,6 @@ public function validateSort(RepositoryInterface $object, array $options)
369374
return $options;
370375
}
371376
}
372-
373377
$options['order'] = $this->_prefix($object, $options['order'], $inWhitelist);
374378

375379
return $options;

tests/TestCase/Controller/Component/PaginatorComponentTest.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,13 +934,77 @@ public function testValidateSortMultiple()
934934
]
935935
];
936936
$result = $this->Paginator->validateSort($model, $options);
937+
937938
$expected = [
938939
'model.author_id' => 'asc',
939940
'model.title' => 'asc'
940941
];
941942

942943
$this->assertEquals($expected, $result['order']);
943944
}
945+
946+
/**
947+
* test that multiple sort works in combination with query.
948+
*
949+
* @return void
950+
*/
951+
public function testValidateSortMultipleWithQuery()
952+
{
953+
$this->loadFixtures('Posts');
954+
$table = TableRegistry::get('PaginatorPosts');
955+
956+
$titleExtractor = function ($result) {
957+
$ids = [];
958+
foreach ($result as $record) {
959+
$ids[] = $record->title;
960+
}
961+
return $ids;
962+
};
963+
964+
// Define two columns as default sort order
965+
$settings = [
966+
'order' => [
967+
'author_id' => 'asc',
968+
'title' => 'asc'
969+
]
970+
];
971+
972+
// Test with empty query
973+
$this->request->query = [];
974+
$result = $this->Paginator->paginate($table, $settings);
975+
$this->assertCount(3, $result, '3 rows should come back');
976+
$this->assertEquals(['First Post', 'Third Post', 'Second Post'], $titleExtractor($result));
977+
$this->assertEquals(
978+
['PaginatorPosts.author_id' => 'asc', 'PaginatorPosts.title' => 'asc'],
979+
$this->request->params['paging']['PaginatorPosts']['totalOrder']
980+
);
981+
982+
// Test overwriting a sort field defined in the settings
983+
$this->request->query = [
984+
'sort' => 'author_id',
985+
'direction' => 'desc'
986+
];
987+
$result = $this->Paginator->paginate($table, $settings);
988+
$this->assertCount(3, $result, '3 rows should come back');
989+
$this->assertEquals(['Second Post', 'First Post', 'Third Post'], $titleExtractor($result));
990+
$this->assertEquals(
991+
['PaginatorPosts.author_id' => 'desc', 'PaginatorPosts.title' => 'asc'],
992+
$this->request->params['paging']['PaginatorPosts']['totalOrder']
993+
);
994+
995+
// Test sorting by a field not defined in the settings
996+
$this->request->query = [
997+
'sort' => 'id',
998+
'direction' => 'asc'
999+
];
1000+
$result = $this->Paginator->paginate($table, $settings);
1001+
$this->assertCount(3, $result, '3 rows should come back');
1002+
$this->assertEquals(['First Post', 'Second Post', 'Third Post'], $titleExtractor($result));
1003+
$this->assertEquals(
1004+
['PaginatorPosts.id' => 'asc', 'PaginatorPosts.author_id' => 'asc', 'PaginatorPosts.title' => 'asc'],
1005+
$this->request->params['paging']['PaginatorPosts']['totalOrder']
1006+
);
1007+
}
9441008

9451009
/**
9461010
* Tests that order strings can used by Paginator

0 commit comments

Comments
 (0)