Sorting items while looping through a collection

I’m fetching my projects to give a list populated items with projects corresponding to the category.

 <?php foreach ($site->find('projets')->children()->filterBy('category', $item->action())->images() as $image) : ?>

gives :

Project 1
  image 1 (filename is a.jpg)
  image 2 (filename is b.jpg)
  image 3 (filename is c.jpg)

Project 2
  image 1 (filename is 1.jpg)
  image 2 (filename is 3.jpg)
  image 3 (filename is 4.jpg)

So right now, these files are displayed correctly by the default sort method, in the correct project order starting with 1 then all the files in it, and then project 2 with all images in it.

But I would like to organise the images in them depending on the defined sorting in my panel.
->sortBy("sort")is usually what I would go with.

 <?php foreach ($site->find('projets')->children()->filterBy('category', $item->action())->images()->sortBy("sort") as $image) : ?>

It gives a mixing of Project 1’s images with images from project 2 . (it seems that the order of the images are the same as in the panel order, though).

Project 1
  image 1 (filename is a.jpg)
Project 2
  image 1 (filename is 1.jpg)
Project 1
  image 3 (filename is c.jpg)
Project 2
  image 2 (filename is 2.jpg)
Project 1
  image 2 (filename is b.jpg)
Project 2
  image 3 (filename is 3.jpg)

I would like to keep the project order, but just display the images in the panel order.

Try:

 <?php foreach ($site->find('projets')->children()->filterBy('category', $item->action())->images()->sortBy('parent', 'sort') as $image) : ?>

Seems that it is working right. Thank you.
Don’t know really why, it’s taking the parent parameter (the project) and sort the child by sort ?

No, it’s sorting the image first by parent then by their sorting number.

What I wonder is why you create this flat list of images also you seem to want to group them by parent and output the parent title, no?

I’m having all of them in a loop for a caroussel.
The logic is sliding all the images of the first project, and as soon as you reach the end the of list, the second project follows.

It’s working nicely right know as the slider is able to update some divs depending on the slide you are seeing.

I’m bumping here since it’s directly related to the first issue:
Actually after using it a bit, it doesn’t really match what I’m trying to achieve.

I order my projects on my homepage, so I would usually go with something like :

<?php $projects = $site->find('projets')->children()->filterBy('category', $item->action())->sortBy('sort');
echo $projects
?>

It works for the projects, but it ignores the way the images are sorted inside the corresponding project.

So I have some images in these projects that I would also like to sort by the way they are organised in the panel.
$projects->images()->sortBy('sort');

images()->sortBy('parent', 'sort'); sorts the image correctly, but ignore the way the parents have also been sorted in the panel.

Any idea?

What is sort in this case? A page usually doesn’t have a sort field…only files.

If I were you, I’d just use two loops

$projects = $site->find('projets')->children()->filterBy('category', $item->action())->sortBy('num');

foreach ($projects as $project) {
  foreach ($project->images()->sortBy('sort') as $image) {
    // do stuff with the image
  }
}

Well, I guess it sorts them in the content folder ?

content:
  projets:
    1_a
    2_c
    3_e

yep, alright, thought it was bad to nest them like that haha. Thanks a lot for the help :slight_smile:

another option if the parent doesn’t work would be a custom file method

Kirby::plugin('my/fileMethods', [
 
    'fileMethods' => [
        'parentNo' => function() {
            return $this->parent()->num();
        }
    ]
]);

that return the parent number

then

<?php $projects = $site->find('projets')->children()->listed()->images()->sortBy('parentNo','sort');

Kirby needs a flatMap() collection method, imho.

// this doesn't exist, yet:
$images = $site
    ->find('projets')
    ->children()
    ->filterBy('category', $item->action())
    ->flatMap(fn($page) => $page->images()->sortBy('sort'));

A crude implementation could be something like:

use Kirby\Cms\App as Kirby;
use Kirby\Cms\Collection;

Kirby::plugin('polyfill/flatmap', [
    'collectionMethods' => [
        'flatMap' => function (Callable $mapFn) {
            $r = [];
            foreach($this as $item) {
                $mapped = $mapFn($item);
                if($mapped instanceOf Collection) {
                    $mapped = $mapped->data();
                }
                if(is_array($mapped)) {
                    array_push($r, ...array_values($mapped));
                } else {
                    array_push($r, $mapped);
                }
            }
            return new Collection($r);
        }
    ]
]);