Further help gathering data from structured fields

With some much appreciated help from @texnixe , @lukasbestle and @AugustMiller on this thread where we got to the following:

<?php
$listings = $site->index()->filterBy('addtocalendar', 'include')->paginate(10);
//loop through the collection of pages
foreach($listings as $listing):
    // fetch the structure field called courses
    $courses = $listing->courses()->toStructure();
       //loop through the structure field collection
       foreach($courses as $course): ?>

       <p><strong><?php echo $courses->course_specific_title()->html() ?></strong>, <?php echo $listing->town()->html() ?>&nbsp;&ndash;&nbsp;<a href="<?php echo url($listing->page()->parent()) ?>">Read full course description</a></p>

   <?php endforeach ?>
<?php endforeach ?>

 <?php if($listings->pagination()->hasNextPage()): ?>
     <p><a class="next" href="<?php echo $listings->pagination()->nextPageURL() ?>">&lsaquo; older posts</a></p>
<?php endif ?>

<?php if($listings->pagination()->hasPrevPage()): ?>
     <p><a class="prev" href="<?php echo $listings->pagination()->prevPageURL() ?>">newer posts &rsaquo;</a></p>
<?php endif ?>

which calls in data from a structured field on specific pages across the site (identified by a switch). I am now wondering how I go about gathering the structured field data as ‘individual entries’ from all the $listings pages? At present it gathers them from the individual $listings pages and groups them together but ideally I require one long list of entries that sortBy('date','asc','town','asc') can be applied to. I’m sure this involves restructuring the loops somehow but just can’t get my head around the logic.

As always, thanks in advance.

Paul

You can do something like this to get a merged collection:

<?php
$listings = $site->index()->filterBy('addtocalendar', 'include')->paginate(10);
$courses = new Structure();
//loop through the collection of pages
foreach($listings as $listing) {
    // fetch the structure field called courses
    $courses = $courses->merge($listing->courses()->toStructure());
}

//loop through the structure field collection
foreach($courses->sortBy('date', 'asc', 'town', 'asc') as $course): ?>
    <p><strong><?php echo $courses->course_specific_title()->html() ?></strong>, <?php echo $listing->town()->html() ?>&nbsp;&ndash;&nbsp;<a href="<?php echo url($listing->page()->parent()) ?>">Read full course description</a></p>
<?php endforeach ?>

 <?php if($listings->pagination()->hasNextPage()): ?>
     <p><a class="next" href="<?php echo $listings->pagination()->nextPageURL() ?>">&lsaquo; older posts</a></p>
<?php endif ?>

<?php if($listings->pagination()->hasPrevPage()): ?>
     <p><a class="prev" href="<?php echo $listings->pagination()->prevPageURL() ?>">newer posts &rsaquo;</a></p>
<?php endif ?>

However please note that you will still only be able to sort the items on the current pagination page, not in the whole collection. For this, you will need to loop through all pages and paginate the $courses collection instead of the page collection.

Aha. Spent a while yesterday trying to merge/append Structure instances into a Collection and couldn’t get things to behave quite right. Didn’t even think that Structure would extend it!

This may come in handy. Thanks, @lukasbestle!

@lukasbestle Have you tested this? I don’t seem to get this to work in a project …

In my (limited) exploration (during which I ended up with something pretty similar), the collection would keep overwriting the same [0] and [1] keys in the Collection… I think? In any case, it would never spit out more than the maximum number of course entries that were inside a single $listing.

Overall, I wonder if it just makes more sense to store courses as sub-pages of each listing? Then you’d be able to depend on more user-facing API functionality.

Yes, exactly, this is the same behavior that I experience as well, the maximum number of entries in the installation was 8 and I never got it to output more then 8 entries.

Oh, that’s right. I have not tested it with Structure, only with another collection that uses unique keys.

Hm…
The reason for this is that a::merge, which is used internally for this, overwrites all keys, even numeric ones. This is against how array_merge would work. The PHP built-in function would probably return a correct result here.

This code is a bit hacky, but could you please try if that works for you?

$listings = $site->index()->filterBy('addtocalendar', 'include')->paginate(10);
$courses = new Structure();
foreach($listings as $listing) {
    // fetch the structure field called courses
    $courses->data = array_merge($courses->data, $listing->courses()->toStructure()->data);
}

If it does, we should maybe consider to implement a new method for Collections and Structures that always appends new elements instead of overwriting them.

Not had a chance to test this yet, been away from my desk so far, will let you know the results as soon as I get the chance

That did not work, but after some more testing I found that this seems to work (hope I changed all the variables correctly):

<?php
$listings = $site->index()->filterBy('addtocalendar', 'include')->paginate(10);
$courses = new Structure();
$key = 0;
  foreach ($listings as $listing) {
    foreach ($listing->courses()->toStructure() as $course) {
      $courses->append($key, $course);
      $key++;
     }
  }

foreach($courses->sortBy('date', 'asc', 'town', 'asc') as $course): ?>
    <p><strong><?php echo $courses->course_specific_title()->html() ?></strong>, <?php echo $listing->town()->html() ?>&nbsp;&ndash;&nbsp;<a href="<?php echo url($listing->page()->parent()) ?>">Read full course description</a></p>
<?php endforeach ?>

I’d prefer an easier approach then using two foreach loops.

Have just tested this in the context of my particular requirements and at first glance this seems to be exactly what I require, will thoroughly test it later but thanks for all your work and suggestions, beers on me everybody :beers::beers::beers: