-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Paginator sortMap. #18898
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 5.next
Are you sure you want to change the base?
Paginator sortMap. #18898
Conversation
Looks good, should we handle what i mentioned here: #18897 (comment) (sharing the same field name on multiple sortmaps) |
I don't think we need validation to prevent "overlapping" fields since each mapped key represents a complete, independent sorting strategy. This is actually a feature - it lets you create multiple sort options that include The concern about ambiguity is resolved by the design:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces a new sortMap
feature to the NumericPaginator class that allows creating friendly sort keys that map to one or more actual database fields. The implementation provides advanced sorting capabilities including simple 1:1 mappings, multi-column sorting, fixed direction sorting, and shorthand syntax support.
Key changes include:
- Added
sortMap
configuration option to allow mapping user-friendly sort keys to database fields - Implemented support for multiple sorting patterns including simple mapping, multi-column sorting, and fixed directions
- Enhanced test coverage with comprehensive test cases for all sortMap functionality
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
File | Description |
---|---|
src/Datasource/Paging/NumericPaginator.php | Added sortMap configuration and resolveSortMapping method to handle mapped sorting logic |
tests/TestCase/Datasource/Paging/NumericPaginatorTest.php | Added comprehensive test cases covering all sortMap functionality scenarios |
tests/TestCase/Datasource/Paging/PaginatorTestTrait.php | Updated test expectations to include sortMap null default in merged options |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
// Check sortMap first for mapped sorting | ||
if (isset($options['sortMap'])) { | ||
$mappedOrder = $this->resolveSortMapping($options['sort'], $options['sortMap'], $direction); | ||
if ($mappedOrder !== null) { | ||
// Use mapped order and merge with existing order | ||
$existingOrder = isset($options['order']) && is_array($options['order']) ? $options['order'] : []; | ||
$options['order'] = $mappedOrder + $existingOrder; | ||
} else { | ||
// Sort key not in sortMap, clear sort | ||
$options['order'] = []; | ||
$options['sort'] = null; | ||
unset($options['direction']); | ||
|
||
return $options; | ||
} | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The logic for handling sortMap vs traditional sorting creates nested conditional blocks that reduce readability. Consider extracting the sortMap handling into a separate method to improve code organization and maintainability.
Copilot uses AI. Check for mistakes.
Hmm, i'm probably not understanding it completely but 'sortMap' => [
'item1' => ['title', 'created']
'item2' => ['title', 'modified']
]
// sorting on both definitions:
&item1=asc&item2=desc Translates into the following statement right ? SORT BY
title ASC
created ASC,
title DESC,
modified DESC |
The paginatorhelper doesn't generate query strings like this. The direction can be specific only for 1 field, |
Imo we dont even need even sort asc/desc if we bind that into the key.
A flat list with all possible options |
Yes that could be an additional enhancement, though what I would really like is the ability to do multi field/colum sort. The query string for it could be like |
@ADmad I think that kind of sort style is not very readable for the end user. This is something Drupal (facets/views) does and it is constantly a fight to get right for proper SEO Maybe something like With that said, we should of course be weary not to go back to Cake 1-2.x style query params. Good times... 🫠 Another thought that crosses my mind is then making sure that it is always returned in the same order to keep a canonical structure. |
The average non-techie user doesn't read/understand query string params :)
I am not knowledgeable enough about how search engines deal with query string these days to be able to comment on this.
This could work I guess. BTW if for e.g. you have a search form with a multi-value field and using PRG you will get |
Yes. You ask a bad question, you get a bad answer. The SQL you posted is syntactically correct, and is what I would expect to happen if all of those ordering clauses were combined.
This could work but we'll need to make
No thank you 😄 |
* - `sortMap` - A map of sort keys to their corresponding database fields. Allows | ||
* creating friendly sort keys that map to one or more actual fields. When defined, | ||
* only the mapped keys will be sortable. Supports simple mapping, multi-column | ||
* sorting, and fixed direction sorting. You can also use numeric arrays for 1:1 | ||
* mappings where the field name is the same as the sort key. Example: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has some overlap with sortableFields
should we keep both long term?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sortableFields
does become redundant with the addition of sortMap
, so it could be deprecated.
I added a demo of combined sort & dir. |
For me it looks quite complete. What's Missing: A way to specify default directions that can still be toggled. For example:
Potential Solution: We could enhance the syntax to support default directions that flip together: 'sortMap' => [
// New syntax: use '@' prefix for toggleable fields with defaults
'newest' => [
'@title' => 'asc', // Default asc, flips to desc
'@created' => 'desc', // Default desc, flips to asc
],
] When user clicks the sort link:
We could also invert this and make the @ signal that this is hardcoding the direction, as this might be more rare used.
Use ! to signal that this is hardcoded in that direction. |
I like the idea! |
Could we have classes to define how a field should be ordered instead of a new syntax? class OrderField
{
public function defaultDir(): ?string
{
}
public function isFixedDir(): bool
{
}
public function ormColumn(): array
} |
As opt-in? I feel like as default this could become quite some overhead for apps to create. |
or do you mean sth like namespace Cake\Datasource\Paging;
class SortField
{
protected string $field;
protected ?string $defaultDirection;
protected bool $locked;
public function __construct(string $field, ?string $defaultDirection = null, bool $locked = false)
{
$this->field = $field;
$this->defaultDirection = $defaultDirection;
$this->locked = $locked;
}
public static function asc(string $field): self
{
return new self($field, 'asc', false);
}
public static function desc(string $field): self
{
return new self($field, 'desc', false);
}
public static function locked(string $field, string $direction): self
{
return new self($field, $direction, true);
}
public function getField(): string
{
return $this->field;
}
public function getDirection(string $requestedDirection, bool $directionSpecified): string
{
if ($this->locked) {
return $this->defaultDirection;
}
if (!$directionSpecified && $this->defaultDirection) {
return $this->defaultDirection;
}
return $requestedDirection;
}
public function isLocked(): bool
{
return $this->locked;
}
} Usage Examples: 'sortMap' => [
'newest' => [
SortField::desc('created'), // Default desc, toggleable
SortField::asc('title'), // Default asc, toggleable
],
'popular' => [
SortField::locked('score', 'desc'), // Always desc
'author', // Still support strings for BC
],
] |
Yes, I like that. Maybe add an interface for it with methods I'm not sure if the methods |
We could maybe also create factory class for building the sortmap then. This would allow for a more strict and readable way for defining them. $factory = SortMapFactory::create()
->field('title')
->field('created')
->field('rank', locked: true, default: 'desc')
->combo('newest', [
SortField::desc('created'),
SortField::asc('title'),
])
->combo('popular', [
SortField::desc('score'),
SortField::asc('comments_count'),
])
->combo('recently-updated', [
SortField::desc('modified'),
SortField::asc('title'),
])
->raw('alpha-group', [
'group_name' => 'asc',
'name' => 'asc',
]); |
Resolves #18897
Feel free to further adjust.