Group foreach items from collection

dear community,
I got stuck with following issue:

the plan is to get an overview/programme/lineup of all project-dates - the projects are child pages and each project-page contains a structure field with date / time / location …
@texnixe helped me a lot sorting things out - see here:

I’d like to have the output like this:

September 2022 (month)

    23. (day)
  • Project A, 20:00
  • Project B, 21:00

Oktober 2022

    10.
  • Project B, 20:00
    15.
  • Project B, 20:00
and so on ...

with my current code it looks like this:

September 2022 (month)

    23. (day)
  • Project A, 20:00
  • Project B, 21:00

Oktober 2022

    10.
  • Project B, 20:00
Oktober 2022
    15.
  • Project B, 20:00

I’d like to group the months like the days … I’ve tried it with group function but without success :-/

this is my template:

<?php $kirby->collection('lineup'); ?>

<?php foreach ($kirby->collection('lineup') as $date => $items):

    $groupedMonths = $items->sortBy('datum')->group(function($p) {
      return $p->datum()->toDate('Y-m');
      }); ?>  

          <h3>
          <?= $groupedMonths ?>
          </h3>
  
    <?php   
    $groupedDays = $items->sortBy('datum')->group(function($p) {
      return $p->datum()->toDate('d.');
      }); ?>
          
          <p>
          <?= $groupedDays ?>
          </p>
      
    <?php foreach ($items as $project): ?>
    <?= $project->title() ?>
    <?= $project->untertitel(); ?>
    <?= $project->zeit() ?>
    <?= $project->zusatz() ?>
    <?php endforeach ?>          

<?php endforeach ?>

this is my collection:

<?php

return function ($site) {
    $projects = $site->find('produktionen')->children()->listed();
    $collection = [];

    foreach ($projects as $project) {
        foreach ($project->termine()->toStructure() as $item) {
            $collection[] = [
                'slug'     => $project->slug() . '-' . $item->datum()->toDate('Y-m-d'),
                'template' => $project->intendedTemplate()->name(),
                'content'  => array_merge(
                    $project->content()->toArray(),
                    ['datum' => $item->datum()->toDate('Y-m-d'),
                     'zeit' => $item->zeit(),
                     'zusatz' => $item->zusatz()]
                ),
            ];
        }
    }
  

  return Pages::factory($collection)->sortBy('datum', 'asc')->groupBy('datum', 'asc');
  };

any help would be appreciated!

You need to group the collection by month in this case, then within each month collection, group by day.

So you got to start at the collection definition.

so do I need to create two different collections - one for months and one for days?

I tried to create a collection for months, but I am not sure, if this is correct …?
this is the dump output:

Array
(
    [0] => Array
        (
            [0] => 2022-12
        )

    [1] => Array
        (
            [0] => 2022-12
        )

    [2] => Array
        (
            [0] => 2022-12
        )

    [3] => Array
        (
            [0] => 2023-01
        )

    [4] => Array
        (
            [0] => 2023-01
        )

    [5] => Array
        (
            [0] => 2023-01
        )

    [6] => Array
        (
            [0] => 2023-01
        )

    [7] => Array
        (
            [0] => 2023-01
        )

    [8] => Array
        (
            [0] => 2022-09
        )

    [9] => Array
        (
            [0] => 2022-09
        )

    [10] => Array
        (
            [0] => 2022-10
        )

    [11] => Array
        (
            [0] => 2022-10
        )

    [12] => Array
        (
            [0] => 2022-10
        )

    [13] => Array
        (
            [0] => 2022-10
        )

)

this is the collection month.php:

<?php 

return function ($site) {
  $projects = $site->find('produktionen')->children()->listed();

  foreach ($projects as $project) {
    foreach ($project->termine()->toStructure() as $item) {
        $collection[] = [
            $item->datum()->toDate('Y-m')
        ];
            }
  }
  
  return $collection;
};

I am not sure, but I think things like sortBy and groupBy don’t work with arrays …?

thx for any advice!

Why didn’t you create the collection exactly as originally, with the only difference being the date formated by Y-m instead Y-m-d?

Yes, of course not, sortBy and groupBy are member methods of their class and can only be used with objects or that class.

yes, that’s what I tried first, but then I got the following output (with the template I postet above):

2022-09 (month)

  1. (day)
    Project A
    Project A

2022-10 (month)

  1. (day)
    Project A
    Project B
    and so on …

it seems $groupedDays can`t get the whole date …?

Sorry, I got confused myself now.

The collection has to be created a originally with the full date. But then do not group the collection or group by month:

<?php

return function ($site) {
    $projects = $site->find('produktionen')->children()->listed();
    $collection = [];

    foreach ($projects as $project) {
        foreach ($project->termine()->toStructure() as $item) {
            $collection[] = [
                'slug'     => $project->slug() . '-' . $item->date()->toDate(),
                'template' => $project->intendedTemplate()->name(),
                'content'  => array_merge(
                    $project->content()->toArray(),
                    ['date' => $item->date()->date('Y-m-d')]
                ),
            ];
        }
    }

    return Pages::factory($collection)->sortBy('date')->groupBy('date');

woyzeck
7d

finally :grinning: it works :wink:

this is my collection-file:

<?php

return function ($site) {
    $projects = $site->find('produktionen')->children()->listed();
    $collection = [];

    foreach ($projects as $project) {
        foreach ($project->termine()->toStructure() as $item) {
            $collection[] = [
                'slug'     => $project->slug() . '-' . $item->datum()->toDate('EE, dd. MMM YYYY'),
                'template' => $project->intendedTemplate()->name(),
                'content'  => array_merge(
                    $project->content()->toArray(),
                    ['datum' => $item->datum()->toDate('EE, dd. MMM YYYY'),
                     'zeit' => $item->zeit(),
                     'zusatz' => $item->zusatz()]
                ),
            ];
        }
    }
  

  return Pages::factory($collection)->sortBy('datum', 'asc');
};

With that you have a standard ungrouped pages collection, one page for each date.

Now in your template, you can group this collection by month:

<?php $collection = collection('mycollection'); ?>
<?php  foreach($collection->group(fn ($p) => $p->date()->toDate('Y-m')) as $month => $items): ?>
    <?= $month ?>
    <?php foreach ($items->groupBy('date') as $day => $dayItems) : ?>
        <?= $day ?>
        <?php foreach ($dayItems as $event): ?>
            <?= $event->title() ?>
        <?php endforeach ?>
    <?php endforeach ?>
<?php endforeach ?>

(Not tested)

good morning and thx again for your patience!

the collection works as expected:

Kirby\Cms\Collection Object
(
    [0] => 2022-09-29
    [1] => 2022-09-30
    [2] => 2022-10-01
    [3] => 2022-10-06
    [4] => 2022-10-07
    [5] => 2022-12-16
    [6] => 2022-12-17
    [7] => 2022-12-31
    [8] => 2023-01-06
    [9] => 2023-01-07
    [10] => 2023-01-12
    [11] => 2023-01-13
    [12] => 2023-01-14
)

unfortunately i get an error Call to a member function toDate() on null for this line:

<?php  foreach($collection->group(fn ($p) => $p->date()->toDate('Y-m')) as $month => $items): ?>

this seems to be really tricky … :see_no_evil:

Rather you seem to have named the field differently maybe, datum instead of date?

yes I did, but I edited the names according to my field-names …

If you want, send me a download link via PM to your project, then I can look into it instead of having to set up a test case myself.

you’ve got mail :wink: thx!!!

it works!!!
deleting ->groupBy('date') in the collection brought success!

THX A LOT!!!