Jump to content

Sort results by title ascending, when title includes special characters (croatian letters)


Moebius
 Share

Recommended Posts

I need to sort results by title ascending, and title includes special characters (croatian letters).

The problem is that results are sorted but not 100% correct:

e.g.

$authors = $pages->find("template=author,sort=title");
  • Cavali
  • Cavilon
  • Čavlović
  • Cesare
  • ...

You see intruder (bolded), it looks like sort engine treats letters C and Č as same ones.
I'm not sure that this is Processwire related, but maybe API has solution for this kind of behavior?

I have UTF-8 page content type .

 

 

 

Link to comment
Share on other sites

It depends on where in the sort order you want the special characters to go. Without doing anything special PHP would sort special characters after ASCII characters, so for instance that would place "Čavlović" after "Zola". If that is what you want (and I doubt that it is) it seems that you can achieve this kind of sort by using the "useSortsAfter" option for PageFinder:

$authors = $pages->find("template=author, sort=title", ['useSortsAfter' => true]);

BTW, it's far from clear to me what the "useSortsAfter" option does exactly.

But I don't think that is what you want anyway. To get a language-aware sort I think you would have to use something like PHP's Collator class - others may know better but I don't think PW has anything built in for this.

So here is something that might work, but it would mean you must get all your authors in one $pages->find() - no pagination in other words:

// Find the author pages
$authors = $pages->find("template=author");

// Get an array where key is author page ID and value is author page title
$titles = $authors->explode('title', ['key' => 'id']);
// New Collator instance
$collator = new \Collator('hr_HR'); // Croatian locale
// Apply language-aware sort
$collator->asort($titles);

// Apply custom sort property to author pages
$i = 1;
foreach($titles as $id => $title) {
    $author = $authors->get("id=$id");
    $author->custom_sort = $i;
    $i++;
}
// Sort authors by custom sort property
$authors->sort('custom_sort');

// Now loop over $authors and output markup

 

  • Like 2
Link to comment
Share on other sites

in my case pagination is not issue, I'll use Robin's solution for initial sort, save sort result value in new page custom field and then sort like we always do:

$authors = $pages->find("template=author,sort=my_new_sort_field");

 

works like a charm.

Link to comment
Share on other sites

8 hours ago, DaveP said:
On 22/11/2017 at 12:21 PM, Robin S said:

get all your authors in one $pages->find() - no pagination in other words:

I think this Gist by @Soma may be of some help.

My point about pagination was really that if you have a large number of authors you cannot apply a limit and must load all results into memory and loop over them to apply the sort. The options for "pseudo-pagination" don't get around this problem unfortunately.

 

3 hours ago, Moebius said:

I'll use Robin's solution for initial sort, save sort result value in new page custom field and then sort like we always do

The thing with that approach is that you would have to use a hook and loop over all author pages every time a new author is added or an author's name is edited. Sounds like that wouldn't be a big deal in your situation, but would be a problem if there were thousands of authors.

Thinking about a solution for large numbers of pages, I had an idea about generating a value for a hidden sort field based on the first letter of the author's name. It would go something like this...

In /site/config.php

// Prefixes for sort field
$config->sortChars = [
    'č' => 'czzzz', // sort after 'c'
    'đ' => 'daaaa', // sort before 'd'
    // etc
];

In /site/ready.php

$pages->addHookAfter('saveReady', function(HookEvent $event) {
    $page = $event->arguments(0);
    if($page->template != 'author') return;
    // Assuming author's last name is in Title field
    $initial = mb_strtolower(mb_substr($page->title, 0, 1));
    if(isset($this->config->sortChars[$initial])) {
        // If the initial letter matches a key in the sortChars array
        // add the prefix to the title and set to the sort field
        $page->sort_field = $this->config->sortChars[$initial] . $page->title;
    } else {
        // Otherwise use the title for the sort field
        $page->sort_field = $page->title;
    }
});

Then use sort=sort_field in the $pages->find() selector, and limiting/pagination is also possible.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...