Sorting by language specific values in a page controller

When I use sortBy() on a pages object in one of my page controllers, the sorting doesn’t take the current page language into account. As far as I read in the forum, sortBy() automatically takes the current “language context” into account. Is this not available in page controllers?

This is what I want to do in one of my page controllers:

// Get all talk sessions and sort them
$talkSessions = $site
    ->index()
    ->filterBy("intendedTemplate", "talk-session")
    ->sortBy(
        "start",
        "asc",
        "room.floor",
        "asc",
        "room.title",
        "asc",
        "parent.title",
        "asc"
    );

When I check the frontend, I see that the parent.title sorting is always done using the default language title version of the pages instead of the respective translated versions for each language.

Probably, the three other sorting rounds are done with the default language values, too. But in those cases, it is not relevant because those fields are not translatable.

Am I missing anything here?

Yes, Kirby should sort the pages according to the values in the current language. And while I only tested a very simple example in the Starterkit’s notes page (German/English)…

$page->children()->listed()->sortBy('title', 'desc')

… this worked as expected.

Are these all field names?

Hm, obviously again something I have done wrong. :wink: Sorry, it’s still my first real Kirby site.

Setup

The setup of templates/pages is:

talks (parent for all talk pages)
↳ talk (one page per talk)
  ↳ talk-session (each talk can have multiple sessions)
rooms (parent for all talk pages)
↳ room (one page per room)

The above-mentioned code is part of the controller for the talks page template.

  • start is a time field in the talk-session blueprint
  • room is a mandatory pages select field in the talk-session blueprint to select one room per talk session
  • room.floor is a mandatory select field in the room blueprint to select the floor where the room is located
  • room.title is the title of the room page
  • parent.title is the title of the talk page which is the parent to the talk session page

On the talks page I list all talk sessions. (I do this grouped by start time using an associative array, but that has nothing to do with the sorting.) And the basis for this list is the above mentioned code.

Test

For testing purposes right now …

  • all talks have only one session,
  • all talk sessions have the same start time,
  • all talk session take place in the same room.

So, if I use the following code directly in the talks pages template (so it is definitely executed within in the correct language context), I should get an alphabetically sorted list of all talk titles – because in this test the first three sorting rules don’t deliver a clear order.

// Just to be sure, what language version are we in?
echo "<h2>Language: ". $kirby->language()->code() . "</h2>";

// Get all talk sessions and sort them
$talkSessionsTest = $site
  ->index()
  ->filterBy("intendedTemplate", "talk-session")
  ->sortBy(
    "start", // In this test: No influence on the sorting because all start times are the same. 
    "asc",
    "room.floor", // In this test: No influence on the sorting because all talk sessions take place in the same room.
    "asc",
    "room.title", // In this test: No influence on the sorting because all talk sessions take place in the same room.
    "asc",
    "parent.title",
    "asc"
  );

// Output the sorted talk sessions using the title of their parent page
foreach ($talkSessionsTest as $talkSessionTest) {
  echo "<p>" . $talkSessionTest->parent()->title() . "</p>";
}

On the English talks page, the resulting talk title list is alphabetically ordered:

Language: en

A-SPICE
AI-Driven Software Development
AI Tools in Use for Marketing
Automated Testing of the Battery Charge Cycle
Clinical Evaluation
Clinical Expertise
CUT Site Support
[…]

On the German talks page, however, the result list is not in alphabetic order:

Language: de

A-SPICE
KI-gesteuerte Software-Entwicklung
KI-Tools im Marketing-Einsatz
Automatisierte Tests des Batterieladezyklus
Klinische Bewertung
Klinische Expertise
CUT-Site-Support
[…]

If you compare the results, you see that the German result list is sorted by the English titles of the talks.

Apparently, the current language context is omitted when I use a field from the parent page ("parent.title") as a sorting rule. If I use fields from the pages I want to sort, the language is correctly considered.

If these are fields in other pages (in this case in room), then it doesn’t make any sense to sort by it, at least not with this syntax. You can only sort by fields (or methods) of the page types that are part of the collection or you have to use a callback. Where did you get this from?

You are right, the room sorting doesn’t work. I was sure, I had tested this with some demo data. But obviously not.

OK. Then I will have to try working with callbacks.

I found a much simpler solution. I ditched the room sorting. Now I only sort talks directly by the start time of a talk’s earliest session, and then by the talk’s title.

To achieve that, I created a new method in the model for talk pages. This method outputs the start time of a talk’s earliest session:

class TalkPage extends Page {
    public function earliestTalkSessionStart() {
        // Get all talk sessions for the current talk
        $talkSessions = $this->children()->filterBy(
            "intendedTemplate",
            "talk-session"
        );

        // If no talk sessions exist, return early
        if (!$talkSessions || $talkSessions->isEmpty()) {
            return;
        }

        // Find the earliest start time
        $earliestStart = "";
        foreach ($talkSessions as $talkSession) {
            $start = $talkSession->start()->toDate("H:i:s");
            if ($earliestStart === "" || $start < $earliestStart) {
                $earliestStart = $start;
            }
        }

        return $earliestStart;
    }
}

earliestTalkSessionStart() can now be used as a sorting criterion for collections which contain talk pages:

$talksSorted = $talks->sortBy(
  "earliestTalkSessionStart",
  "asc",
  "title",
  "asc"
);

No need to mess around with talk sessions anymore.

Some notes:

The first condition is superfluous, $talkSessions will always be a collection.

Can be shortened to:

$earliestStart =  $talkSessions->sortBy('start')->first()?->start()->toDate('H:i:s');