Mix and sort multiple models/blueprints

Hi.

I’m looking to set up a calendar that can order content from different page templates.

I have several blueprints (shows and rendezvous), which have date informations and are stored in multiple directories. In shows, the dates are stored in a structure (several dates are possible). In rendezvous, only one date is possible.

I’d like to mix these two types of contents together and to sort them by date, so that I have:

  • 26/04/2020
    • 19:00 – [rendezvous 1]
    • 20:30 – [show 1]
    • 22:00 – [rendezvous 2]
  • 28/04/2020
    • 14:30 – [show 2]
    • 20:30 – [show 1]
  • 05/05/2020
    • 15:30 – [rendezvous 3]

Notes:

  • I may have performance issues (relying on $site->index() filtered by templates array), because there will be a lot of events
  • All events should be future events (not difficult, but I failed to solve via custom pages models)
  • I must duplicate shows display on multiple days, according to each date.

I can’t figure out where to start…
If anyone here can help me, thank you in advance.

What numbers are we talking here? A custom cache for the events might be useful if there are two many. Avoid $site->index() if possible.

Yes, you can merge structure items with pages by creating a single collection from array items.

How exactly are these stored? Could you post the blueprint?

Hi Sonja.

Thank you for your answer.
Regarding the potential performance issues, I’ll try to deal with a custom cache.

Shows belong to a “season”, they’re stored in /content/seasons/2019-2020/. Rendezvous are in /content/rendezvous/. To group shows by day, I used the solution you provided here: Create a collection of pages from a structure field.

# controllers/calendar.php

return function ($site, $page, $kirby) {

  // get future shows
  $shows = page("seasons/2019-2020")->children()->listed()->filter(function ($child) {
      # what should I use to grab only future shows ?
  });

  // get all dates
  foreach($shows as $show) {
      $dates = $show->dates()->toStructure()->pluck('date');
      sort($dates);
      foreach($dates as $date) {
          $mydates[] = strtotime($date);
      }        
  }

  // deduplicate + sort
  $mydates = array_unique($mydates, SORT_NUMERIC);
  sort($mydates);

  return [
      'shows' => $shows,
      'dates' => $mydates
  ];
};

function getShowsPerDate($shows, $day) {
    $showsPerDay = $shows->filter(function($child) use($day) {
        $dates = $child->dates()->toStructure()->pluck('date');
        foreach($dates as $date) {
            $strDates[] = strtotime($date);
        }
        // check if the current day is in dates
        if(in_array($day, $strDates)) {
            return $child;
        }
    });
    return $showsPerDay;
}

Then, in the template:

<?php foreach($dates as $date) :
    $dayShows = getShowsPerDate($shows, $date); ?>
    <?php foreach($dayShows as $show) :?>
        (…)

This logic works for shows (even if I thought I’d better have the final data prepared in the controller without calling getShowsPerDate in the loop), but I don’t know where neither how to plug the rendezvous…

Sure. Nothing fancy here (I simplified):

# show.yml
fields:
  dates:
    label: Dates
    type: structure
    fields:
      place:
        type: text
      date:
        type: date
        label: Date
        time: true
# rendezvous.yml
fields:
  place:
    type: text
  date:
    type: date
    label: Date
    time: true

Thanks a lot for your help. I hope I’ve been clear enough.