Grouping collections in Kirby 5

Hello everyone,

I am unsure whether I am making a mistake, am simply unaware of a breaking change, or if there is a yet problem with kirby 5.0.0-beta.2 . So, I am seeking some help and clarification here.

I tried out kirby 5.0.0-beta.2 on a website which works fine with kirby 4.6.0. i was delighted to find that everything works, apart from a single page that makes use of a collection that is grouped by date. The $date field in the content is formatted in a callback identically to the cookbook example.

I dumped the collection and in 4.6.0 the collection is split correctly by date:

Kirby\Toolkit\Collection Object
(
    [0] => 2025-03-25
    [1] => 2025-03-26
)

In 5.0.0-beta.2 the collection is unchanged, simply a long list of page objects.

Kirby\Cms\Collection Object
(
    [0] => imps-2025/abstracts/title-1
    [1] => imps-2025/abstracts/title-2
    [2] => imps-2025/abstracts/title-3
 ...
)

In summary, is this a bug due to the beta status that needs fixing, or has something changed that I am unaware of?

Feedback would be appreciated. Cheers.

Could you please post your code as well? No change in v5 jumps directly to mind that should cause a difference with 4.6 - so need to see better what code is used to see if it rings any other bell.

Hello @distantnative and thank you for your interest.

For your understanding, the page is creating a programme for a congress / symposium that is called the IMPS. It grabs all information from a sibling page that is called abstracts. The abstracts page has a bunch of children with the information on the talks. Each of the children represents one talk and contains information such as talk title, date (date and time), speaker details and an summary. The programme page grabs that information and displays it in an overview. The production page runs on kirby 4.6.0 and can be seen here.

These are the relevant requested files:

The ‘abstracts’ collection file:

return function ($site) {

        // create a collection of all abstracts in this IMPS.
        if ($site->page()->parent()->hasTemplate('imps') and $site->page()->siblings()->template('abstracts')->children()->listed()) {
            $collection = $site->page()->siblings()->template('abstracts')->children()->listed()->sortBy('posno', 'asc', 'title', 'asc');
        }
        else {
            $collection = null;
        }

    return $collection;
};

The page controller:

return function ($page, $kirby) {

    // get the root of this year's / this particular IMPS
    $imps = $page->parent();

    // get the collection of abstracts to obtain the information for the talks
    $abstracts = $kirby->collection('abstracts');
    
    // Error if no abstracts found
    if($abstracts->isEmpty() or $abstracts->count() == 0) {
        throw new Exception("Controller Error: No abstract pages found for this IMPS.");
    }

    // sort talks by date
    $abstracts = $abstracts->sortBy('date', 'asc');

    // filter all abstracts by the date. The date parameter has to be present to group by in the next step.
    $abstracts = $abstracts->filter(
        fn ($page) => $page->content()->date()->isNotEmpty()
    );
    
    // function that returns the formatted date reduced to the date without the time so that the talks/abstracts can be grouped by day.
    $callback = function($f) {
        if($f->date() && $f->date()->isNotEmpty()) {
            return $f->date()->toDate('yyyy-MM-dd'); /* appears to require ICU datetime format. */
        } else {
            return 'undated';
        }
    };

    // group abstracts by the date field in the abstracts content using the callback filter above.
    $days = $abstracts->group($callback);

    // get the coffee breaks and other events
    $breaks = $page->breaks()->toStructure()->sortBy('date', 'asc');

    return [
        'imps' => $imps,
        'days' => $days,
        'breaks' => $breaks
    ];

The page itself is only some HTML containers and an include statement for the relevant code snippet. For my debugging efforts I have included a single line of code at the top of the snippet that dumps the $days variable and then exits. I therefore assume that all further code is irrelevant.

<?php echo "Days: "; dump($days); exit(); ?>

I use git to checkout the 4.6.0 tag or the 5.0.0.-beta2 tag on the kirby folder and can thus switch directly between the two without changing any other code.

$ website/kirby: git checkout tags/4.6.0
$ website/kirby: git checkout tags/5.0.0-beta.2 

The resulting dump shows the information I stated in my original question shown above. The collections groups as expected in 4.6.0 but does not do so in 5.0.0

The only complicated thing I have done here is that I make use fo the intl date functions in a function of the page model which complicated the formatting strings for all date functions. However, that is called later in the code snippet, after the exit() of my debug dump and thus never executed here. I therefore don’t think it should play a role for this problem. It is the only thing I could think of.

page model:

class ProgrammePage extends Page
{
    // create a cover image
    public function cover()
    {
        return $this->content()->cover()->toFile() ?? $this->image();
    }


    // use the intl date function to show the date in multiple languages
    public function intlDate($d, $format = 'YYYY-MM-dd'): string
    {
        $language = kirby()->language();

        if($language->code() == 'de')
        {
            $fmt = new IntlDateFormatter(
                'de-DE',
                IntlDateFormatter::FULL,
                IntlDateFormatter::FULL,
                'Europe/Berlin',
                IntlDateFormatter::GREGORIAN,
                $format
            );
        } else 
        {
            $fmt = new IntlDateFormatter(
                'en_US',
                IntlDateFormatter::FULL,
                IntlDateFormatter::FULL,
                'Europe/Berlin',
                IntlDateFormatter::GREGORIAN,
                $format
            );
        }
        return $fmt->format($d);
    }
    
}

I hope this helps in finding the :beetle: . Many thanks in advance for your efforts.

As a side note. I also have another page that only lists the titles of the abstracts without grouping them and that page works fine on 5.0.0-beta.2. For me it all points to the group() step.

Just to confirm, there is no error whatsoever, just the collection remains ungrouped?

I can remember that when I switched a project from K4 to K5, I also ran into an issue with grouping, but unfortunately cannot remember what exactly the problem was and how I solved it, but grouping works. Have you deleted the cache and media folders just to be on the safe side?

Hello @texnixe. Yes, that is exactly right. By placing the dump-and-exit command at the top of the code snippet, the code runs without errors in both cases, i.e. for both versions 4.6.0 and 5.0.0-beta.2, just with very different results for the $days variable. Deleting the media folder and cache has not made a difference.

I am not sure if it makes a difference, but the object descriptors are not the same:

Kirby\Toolkit\Collection Object

vs.

Kirby\Cms\Collection Object

I am not sure if that helps. I just point it out in case you overlooked it.

Due to the debugging exit() command, most of my following code does not run. If I run the rest of the code in kirby 5.0.0-beta.2 it does fail because the expected variable content is not in the right format i.e. is incorrect. Any time I switch back to kirby 4.6.0, the code works again.

If you can remember what you did to fix grouping the last time, then that would be great. Also, if you want to point me to anther git branch/tag that is more up-to-date than 5.0.0-beta.2 release tag then I am happy to try that out.

Perhaps you can think of a different way to achieve the same effect as the group() function by using other functions (filter or some such)? That would be an interim solution for me.

However, it does look to me like there is something different between 4.6.0 and 5.0.0, as it works in the former and does not work with the later version. All that I am changing is the version of the kirby folder.

Here is some more information:

If I run groupBy('date') then it works as expected. It just doesn’t help me as the times are different for everything. I need the callback to re-format the date and remove the time component.

   // group abstracts by the date field in the abstracts content using the callback filter above.
    #$days = $abstracts->group($callback);
    $days = $abstracts->groupBy('date');

The resulting $days variable is:

Kirby\Cms\Collection Object
(
    [0] => 2025-03-25 09:00:00
    [1] => 2025-03-25 09:30:00
    [2] => 2025-03-25 10:00:00
    [3] => 2025-03-25 11:15:00
...
)

So in this case it does group as expected, just not in a helpful way.

What if you create a custom method in your model that returns the date in the format you want and then pass that to groupBy()?

1 Like

Problem solved, if perhaps not the underlying bug itself.

If I create a function in the page model of the abstract pages (page hierarchy: imps->abstracts->abstract) then I can call that function (i.e. virtual field) with the groupBy() function in the programme page (page hierarch: imps->programme) and that works.

Here are the relevant files.

the new function in the abstract model file:

    // return only the date without the time for the date field
    public function dateonly()
    {
        return $this->content()->date()->toDate('yyyy-MM-dd');
    }

and here the change to the relevant grouping function in the programme controller:

    /* Function that returns the formatted date reduced to the date without the time so that the talks/abstracts can be grouped by day.
     * Grouping via the callback does not work in kirby 5.0.0-beta.2.
     */
    $callback = function($child) {
            return $child->content()->date()->toDate('yyyy-MM-dd'); // appears to require ICU datetime format.
        };
    

    /* group abstracts by the date field in the abstracts content.
     */
    #$days = $abstracts->groupBy($callback); // this does not work in kirby 5.0.0-beta.2
    $days = $abstracts->groupBy('dateonly'); // group by the (virtual) field that is defined in the abstract model. This works in 5.0.0-beta.2

I am not surprised that this solution works, as it it an alternative solution. However, I am not sure why this works and the original grouping with a callback does not work.

That’s the interesting question… I think I will do some more tests.

I was composing my post as you wrote this. Yes, this works :relieved:. However, I would have expected the other solution to also work—as indeed it does in kirby 4.6.0. Thanks for your help.