Difficulty merging structures into a collection

I’ve read the following threads:

In my site.yml, I have a few structure fields for the client to define a number of tags that are subsequently pulled into a tags field via a query.

site.yml

      people:
        type: structure
        sortBy: title
        width: 1/2
        fields:
          autoid:
            type: hidden
            translate: false
          title:
            type: text
      subjects:
        type: structure
        sortBy: title
        width: 1/2
        fields:
          autoid:
            type: hidden
            translate: false
          title:
            type: text

I’m trying to merge the structures like so:

site/collections/testcollection.php

return function ($site) {
  $structure = new Structure();
  $people = $site->people()->toStructure();
  $subjects = $site->subjects()->toStructure();

  $key = 0;

  // FOREACH A
  foreach ($people as $item) {
    $structure->append($key, $item);
    $key++;
  }

  // FOREACH B
  foreach($subjects as $item) {
    $structure->append($key, $item);
    $key++;
  }

  return $structure;
};

Then I consume the collection in a blueprint:

  tags:
    type: tags
    accept: options
    options: query
    query: kirby.collection('testcollection')

For some reason, only the items from the latter foreach are visible. If I remove the second foreach or change their order, then the items from FOREACH A are visible in the tags field. Am I missing something obvious here about how to properly merge two structure fields into a collection?

Not sure why, but you could do it like tthis instead:

 $structure->add($people);
 $structure->add($subjects);

without loops and keys.

1 Like

Perfect, thank you. It’s actually much easier/cleaner as well than the multiple foreach loops and doesn’t require a plugin either.

I’m ringing in as this is something I’m looking at as well; having a structure on multiple pages which needs to show on an overview-page. How can I iterate over the items of this new structure using a foreach loop to show each item?

@pixelliquid I think it would help if you post what you already have and then describe where it fails. The example for creating such a combined structure is above, then you would loop through this like you would normally.

True. In another thread I asked for help in filtering dates. But then I stumbled upon this thread as I noticed that the items where sorted by page and then date instead of date only; so I figured it had to do with multiple structures after each other instead of merging them in. Merging works, but the I’m lost in how to iterate over the resulting array.

This is the code. I kept the comments for clarity in my thought process:

<h2><?= t('playdates') ?></h2>
  <ul>
    <?php
    // we create new struture to hold the playdate structures from all productions
    $structure = new Structure();
    // we find the playdate structures
    $productions = $site->find('page://YDLYvueOhZwB3glM')->children()->listed();
    foreach ($productions as $production):
      $items = $production->playdates()->toStructure();
    endforeach;
    // we add them to our new structure
    $structure->add($items);
    // we show stuff
    foreach ($items as $item): ?>
      <li>
        <span><?= $item->playdate()->toDate('EEEE d MMMM Y – H:mm') ?></span>
        <span><?= $production->title() ?></span>
        <span><?= $item->location() ?></span>
        <span><?= $item->city() ?></span>
        <?php if ($item->ticketlink()->isNotEmpty()): ?>
          <a href="<?= $item->ticketlink() ?>">tickets</a>
        <?php endif ?>
      </li>
  <?php endforeach ?>
  </ul>

The part from //we show stuff onwards is puzzling me; how can I reach in the new Structure? the resulting error says $item is not defined.

This needs to go inside your first loop, otherwise you only add the last $items

Unfortunately I can’t get it to work properly. It shows all enties, but I can’t sortBy or filterBy - at least there is no change in the resulting list.

<ul>
    <?php
    // we create new struture to hold the playdate structures from all productions
    $structure = new Structure();
    // we find the playdate structures
    $productions = $site->find('page://YDLYvueOhZwB3glM')->children()->listed();
    foreach ($productions as $production):
      $items = $production->playdates()->toStructure();
      // we add them to our new structure
      $structure->add($items)->sortBy('playdate', 'desc');
      // we show stuff
      foreach ($items as $item): ?>
      <li>
        <span><?= $item->playdate()->toDate('EEEE d MMMM Y – H:mm') ?></span>
        <span><?= $production->title() ?></span>
        <span><?= $item->location() ?></span>
        <span><?= $item->city() ?></span>
        <?php if ($item->ticketlink()->isNotEmpty()): ?>
          <a href="<?= $item->ticketlink() ?>">tickets</a>
          <?php endif ?>
        </li>
        <?php endforeach ?>
      <?php endforeach ?>
  </ul>

first I had the foreach loops after each other but then there was no resulting list.

I don’t have time now to correct your code, but it helps if you tell yourself in plain language what you want to do in steps, like

  1. I first want to collect all entries into a structure object+
  2. Then I sort this collection
  3. Then I loop through the results

Then you translate that to code.

Thanks Sonja. Indeed I had it written out see the comments - those were the steps, but hit a wall halfway. I think merging structures for me is a step too far. In fact the translation to (php) code is the bottleneck :slight_smile:

Frustrating is that I have a structure with items but when & where to filter or sort is still a bit magical. Need to spend more time in the reference & guide.

<?php
    // initialize a new structure object
    $structure = new Structure();
    // get all pages with structure field
    $productions = $site->find('page://YDLYvueOhZwB3glM')->children()->listed();
    // loop through all pages and add structure items to the structure object
    foreach ($productions as $production):
      $items = $production->playdates()->toStructure();
      // we add them to our new structure
      $structure->add($items);
    endforeach;
  // only when this is done, we move on to the next step, looping through the newly created structure object, which we can now also sort:
?>
<ul>
  <?php foreach ($structure->sortBy('playdate', 'desc') as $item): ?>
    <li>
      <span><?= $item->playdate()->toDate('EEEE d MMMM Y – H:mm') ?></span>
      <span><?= $production->title() ?></span>
      <span><?= $item->location() ?></span>
      <span><?= $item->city() ?></span>
      <?php if ($item->ticketlink()->isNotEmpty()): ?>
        <a href="<?= $item->ticketlink() ?>">tickets</a>
      <?php endif ?>
    </li>
  <?php endforeach ?>
</ul>

That’s why it is so important to get it right in your head first, before you even start to write code. It’s all about logic and doing steps in the right order. Try to understand why this works and yours doesn’t, and where you went wrong. That’s the first step to your bright future in coding :wink:

1 Like