Filter Structure Field for Single Entry Matching Current Month

Hello,

I have a content folder with 12 children, one for each month of the year.

Each folder contains a blueprint with a structured field.

I want to randomly load one content item from the structure field and display it on the home page.

I also want to draw content from the history folder’s child that matches the current month. So, if the visitor visits in March, they see a single random bit of history content sourced from the structure field from history/march/.

Here is the content structure for the history folder:

history
  |---/01
  |---historyitem.txt
  |---/02
  |---historyitem.txt
  |---/03
  |---historyitem.txt
  |---/04
  |---historyitem.txt
  |---/05
  |---historyitem.txt
  |---/06
  |---historyitem.txt
  |---/07
  |---historyitem.txt
  |---/08
  |---historyitem.txt
  |---/09
  |---historyitem.txt
  |---/10
  |---historyitem.txt
  |---/11
  |---historyitem.txt
  |---/12
  |---historyitem.txt
history.txt

Here is the blueprint:

title : History
fields:
  stories:
    label: Stories
    type: structure
    fields:
      story:
        label: Story
        type: textarea
        size: small
      link:
        label: Link
        type: url
      date:
        label: date
        type: date

The content .txt file:

Title: January

----

Stories:

- 
  story: >
    news story 1
  link: 'https://example1.com'
  date: 1920-11-01
- 
  story: >
    news story 2
  link: 'https://example2.com'
  date: 1943-12-11
---- 

and this is my current template code:

      <?php foreach (page('history')->children() as $item) : ?>
         <?php
         foreach ($item->stories()->toStructure()->shuffle()->limit(1) as $story): ?>
            <?= $story->date()->todate('F j') ?>, <?= $story->date()->todate(' Y') ?>,
            <?= $story->story() ?>
            (<a href="<?= $story->link() ?>">Source</a>)<br>
         <?php endforeach ?>
      <?php endforeach ?>

Almost there… but that code shows a single random entry for every folder. Everything else I tried breaks the code. My guess is that it should be limited or filtered, but I can’t properly figure it out.

How do I code it to show just one entry drawn from the history folder child that matches the current month?

To clarify, at one point of time, you only want to get one historyitem page, and then a random structure item from that history item, right?.

But then you would have to remove the two loops in favor of 1. finding the page you want, and 2, picking a random item

So

<?php if ($p = page('history')->children()->findBy('slug', '02')) {
  $story = $p->stories()->toStructure()->shuffle()->limit(1);
  echo $story->story();
}
?>

Thank you, texnixe.

The information will be displayed on the home page, so there will be no segment after “domain.com/

I am trying to match the month from the date field in the structure with the current date’s month. Something like this:


<?php

foreach (page('history')->children()->filter(fn($child) => $child->date(null, 'F')->toDate() == strtotime('month'))->limit(1)->shuffle() as $item) : ?>
   <?php foreach ($item->stories()->toStructure()->shuffle()->limit(1) as $story): ?>
      <?= $story->date()->todate('F j') ?>, <?= $story->date()->todate(' Y') ?>,
      <?= $story->story() ?>
      (<a href="<?= $story->link() ?>">Source</a>)<br>
   <?php endforeach ?>
<?php endforeach ?>

The code above produces one listing only, but it doesn’t shuffle or match the current month.

Well, the code above was an example where you need to replace the hardcoded month with the current month. I must admit, I don’t understand your code example. If you have only one page that fits a given month, like currently February, what do you want to shuffle there?

If you replace the hardcode 02 in this example with the the current month digit (prepended by 0 for all single digit months (because you are making it more difficult with your slugs…)

Hi, thanks again for your help. I’m new to PHP and Kirby, so there might be a better way to do all this that I haven’t thought about.

After troubleshooting, the improved code below now displays a single random entry.

   <?php
   foreach (page('history')->children()->shuffle()->limit(1) as $item) : ?>
      <?php
      foreach ($item->stories()->toStructure()->limit(1) as $story): ?>
         <?= $story->date()->todate('F j, Y') ?>,
         <?= $story->story() ?>
         (<a href="<?= $story->link() ?>">Source</a>)<br>
      <?php endforeach ?>
   <?php endforeach ?>

This code lives on the home page template.

How could I filter to match the current month to the month in the date of the structure field?

So, as you mentioned, February would only pull content from the February folder, but next month would automatically pull from March without having to re-code the loop.

Should I put all of the content options in one field in one folder and draw from there instead?

The final output shows facts from history related to the current month.

This is what you outlined above. So for me, this means that you want to 1. get history/03, then a random structure item of this child.

Now what you write in your last post:

Now here you suddenly want to filter the history items by date in one of the structure field items.

Totally different stuff.

Really sorry, but cannot really help if what you describe is contradictory.

Forgive me, perhaps I’m using the wrong language.

If there’s a better way to do this, I’m open to trying.

Should I put all of the content options in one structure field in one folder, and then draw from there instead?

Thank you.

I think things would be easier if you describe exactly what you want to achieve. Usually a clear description is half the code written. Like

// First, I need to get the number for the current month

// Then, I want to get the child of history that matches the current month. The slugs of the children are numbered by month


// Finally, I want to fetch a random structure item of the structure field of that page

//...

Or whatever it is that you want to do. Then you can translate this into code.

If you ask Chatgpt something like the above:

Given the following page structure in Kirby CMS:
history
|—/01
|—historyitem.txt
|—/02
|—historyitem.txt
|—/03
|—historyitem.txt
|—/04
|—historyitem.txt
|—/05
|—historyitem.txt
|—/06
|—historyitem.txt
|—/07
|—historyitem.txt
|—/08
|—historyitem.txt
|—/09
|—historyitem.txt
|—/10
|—historyitem.txt
|—/11
|—historyitem.txt
|—/12
|—historyitem.txt
history.txt

I want to get the single child that corresponds to the current month.

Then, I want to get a random item of the structure field of that page, the name of the structure field is story. Finally, I want to echo the date and the story fields of this structure field item.

You get the following answer:

<?php
// Step 1: Get the current month
$currentMonth = date('m'); // This will return the month as 01, 02, ..., 12

// Step 2: Get the child page corresponding to the current month
$historyPage = page('history'); // Assuming 'history' is your parent page
$monthPage = $historyPage->find($currentMonth);

// Check if the month page exists
if ($monthPage) {
    // Step 3: Get a random item from the 'story' structure field
    $storyItems = $monthPage->story()->toStructure(); // 'story' is the structure field

    if ($storyItems->isNotEmpty()) {
        // Pick a random item from the structure field
        $randomStoryItem = $storyItems->shuffle()->first(); // Shuffle and pick the first (randomized)

        // Step 4: Echo the 'date' and 'story' fields
        echo "Date: " . $randomStoryItem->date() . "<br>";
        echo "Story: " . $randomStoryItem->story() . "<br>";
    } else {
        echo "No stories found for this month.";
    }
} else {
    echo "No history page found for this month.";
}
?>

Explanation:

  1. Current Month:
  • We use date('m') to get the current month in the format of two digits (01, 02, …, 12).
  1. Find the Page for the Current Month:
  • The find($currentMonth) method is used to find the child page that matches the current month (e.g., /01, /02, etc.).
  1. Random Story Item:
  • We retrieve the story field using $monthPage->story()->toStructure(), which returns the structure field as a collection of items.
  • We shuffle the collection using the shuffle() method and then use first() to pick a random item.
  1. Display the Date and Story:
  • We echo the date() and story() fields from the selected structure field item.

This code will dynamically show a random story and its date based on the current month.

Again, I don’t know if that’s what you wanted… Just a demonstration that it works.

Okay, I got it to work:

<?php
   // Get the current month (01, 02, 03, etc.)
   $currentMonth = date('m');

   // Fetch the main "history" page
   $historyPage = page('history');

   // Find the subpage corresponding to the current month (e.g., "01", "02", etc.)
   $currentHistoryEntry = $historyPage->children()->find($currentMonth);

   // Fetch the structure field (assuming it's called 'stories')
   $stories = $currentHistoryEntry->stories()->toStructure()->shuffle()->limit(1);

   foreach ($stories as $story): ?>
      <?= $story->date()->todate('F j, Y') ?>, <?= $story->story()->html() ?>
      (<a href="<?= $story->link() ?>">Source</a>)<br>
   <?php endforeach ?>

Hadn’t thought about asking ChatGPT, but will try that from now on.

Thanks for your help!

Sorry, I didn’t want to send you to Chatgpt, I just wanted a clear description what you wanted to achieve.

Your idea of writing a clear description first is helpful.