Access previous image in file collection (manual sorting)

Hi, this is my first post here so please bear with me.

I am trying to use Kirby’s $file->prev() function (see here) to access the previous image in a foreach loop. The problem is that the function seems to ignore the manual sorting parameter and instead sorts by filename. Any idea what I could be missing/doing wrong?

Thanks!

<?php foreach($page->images()->sortBy('sort', 'asc')->offset(1) as $image): ?>
  <p>
  Previous image: <?= $image->prev()->filename(); ?><br>
  This image: <?= $image->filename() ?>
  </p>
<?php endforeach ?>

You need a custom method to achieve that, one that accepts a parameter, it’s the same problem for pages, have a look here: https://github.com/texnixe/kirby-custom-methods/blob/master/page-methods/get-next.php

Thank you. I’ll take a look and report back when I get this working (seems a bit daunting at the moment)…

These should work, if the last index is reached, it starts from the beginning and vice versa; if you don’t want this behaviour, you can change it, so that the functions don’t return anything once they have reached the beginning/end.

file::$methods['getNext'] = function($file, $sort='sort', $direction='asc') {
  $siblings = $file->files()->sortBy($sort, $direction );
  $index  = $siblings->indexOf($file);
  $maxIndex = $siblings->indexOf($siblings->last());
  if($index === false) return false;
  if($index === $maxIndex) return $siblings->nth(0);
  return $siblings->nth($index+1);
};

file::$methods['getPrev'] = function($file, $sort='sort', $direction='asc') {
  $siblings = $file->files()->sortBy($sort, $direction );
  $index  = $siblings->indexOf($file);
  $maxIndex = $siblings->indexOf($siblings->last());
  if($index === false) return false;
  if($index === 0) return $siblings->nth($maxIndex);
  return $siblings->nth($index-1);
};

Usage:

$file = $page->files()->sortBy('sort', 'desc')->first();
$next = $file->getNext('sort', 'desc');

Handed on a silver platter. This works brilliantly, thank you so much! :heart_eyes:

I’m in the process of upgrading the project I used this on to Kirby 3 and things have stopped working. I know get a Call to a member function files() on string error triggered by

$file->files()->offset($offset)->sortBy($sort, $direction );

It’s probably trivial but I’m a bit stuck here… Maybe there are new ways to get prev/next files in a sorted collection by now, too?

Still no dice here. As far as I understand I’m passing an image object to the method, not a string:

<?= $page->images()->first()->getNext('sort', 'asc') ?>`

This is the K3 plugin:

Kirby::plugin('foxacid/getNext', [
    'fileMethods' => [
        'getNext' => function($file, $sort='sort', $direction='asc') {
          $siblings = $file->files()->sortBy($sort, $direction);
          $index  = $siblings->indexOf($file);
          $maxIndex = $siblings->indexOf($siblings->last());
          if(($index === false) || ($index === $maxIndex)) return false;
          return $siblings->nth($index+1);
      }
    ]
]);

You can use prev()/next() with the sorted collection as parameter, see the documentation for those methods.

You can use prev()/next() with the sorted collection as parameter, see the documentation for those methods.

That will work for the simple example above, but not inside a loop:

<?php 

$sortedImages = $page->images()->sortBy('sort', 'asc')->offset(1);

foreach($sortedImages as $image) {
    echo $image->next($sortedImages)
}
?>

:pensive:

What is the point of calling next in this loop? and the code works as expected if you look at this code example:

$sortedImages = $site->index()->images()->sortBy('filename', 'asc')->offset(1);
dump($sortedImages);

foreach($sortedImages as $image) {
  dump($image->id());
  dump($image->next($sortedImages)?->id());
  echo '<hr>';
}

Will always output the current image in the loop and the next one.

1 Like

It’s… complicated.

I’m automating page layouts based on image aspect ratios. For this I check the ratios of previous and next siblings and set layout classes accordingly.

Then please explain what doesn’t work in the above code.

Sorry I checked again and it sure does work. I had a typo in the controller. Many thanks for helping out!