Sorting nextListed

Within my notes.php controller, I am sorting them by multiple fields.

$notes = $page->children()->listed();
$sorted = $notes->sortBy('noteDate', 'desc', 'noteTime', 'desc');

Then I have a prev/next snippet as follows:

<?php if ($page->hasNextListed() || $page->hasPrevListed()): ?>
<!-- TODO: Fix sorting -->
    <nav class="o-prevnext">

        <?php if ($next = $page->nextListed('noteDate', 'desc', 'noteTime', 'desc')): ?>
            <div class="o-prevnext__next">
                <a class="o-prevnext__link" href="<?= $next->url() ?>" rel="next">
                    < <?= $next->headline()->or($next->title()) ?>
                </a>
            </div>
        <?php endif ?>

        <?php if ($prev = $page->prevListed('noteDate', 'desc', 'noteTime', 'desc')): ?>
            <div class="o-prevnext__prev">
                <a class="o-prevnext__link" href="<?= $prev->url() ?>" rel="prev">
                    <?= $prev->headline()->or($prev->title()) ?> >
                </a>
            </div>
        <?php endif ?>

    </nav>
<?php endif ?>

Reading this https://getkirby.com/docs/reference/objects/page/next-listed it says I can sort the outcome.

You can see from my prev/next snippet that I am trying to sort by the same fields I do in my notes controller, but it doesn’t return correctly.

Is this correct? Or do I need to try and somehow do a fancy way of prev/next controls?

If you have the controller set up correctly, it should be listing the reuqired set of filtered pages. No need to filter again on the next and previous links (i think). Basically you should be returning $sorted from your controller and then using that to loop through on your template. Then you can just normal next and previous buttons since the list has been filtered upfront for you.

Can you post your full controller here, and the template you are using?

It says so in the docs, but looking at the source code, there is no parameter used in that function.

As an alternative, you could use the getPrev() and getNext() methods from here: https://github.com/texnixe/k3-pagemethods/blob/master/index.php

1 Like

I guess I should say that the notes controller and the prevnext snippet are independent of each other. I was simply stating that inside my notes controller I am able to sort the articles list by my 2 defined fields via the sortby method. The prevnext snippet was something from the kirby 2 starterkit I pulled over into kirby 3, then changed stuff that was depricated to their new names/functions.

I have a notes.php and a note.php. Notes is the all articles list page, note is the article page. Inside my notes.php it will obviously get all articles and sort them correctly by the 2 defined fields. My note.php is the article page that has a snippet of prevnext as listed above.

@jimbobrjames If you could propose a solution that would be great, I think for now I will have to look at using a custom page method as @texnixe has suggested.

notes.php (controller)

<?php

return function ($page) {

    // Notes
    $notes = $page->children()->listed();
    $sorted = $notes->sortBy('noteDate', 'desc', 'noteTime', 'desc');

    // Categories & Tags
    $categories = $notes->pluck('category', ',', true);
    $categoryurl = $page->url();

    $tags = $notes->pluck('tags', ',', true);

    if ($tag = urldecode(param('tag'))) {
        $sorted = $sorted->filterBy('tags', $tag, ',');
    }

    if ($category = urldecode(param('category'))) {
        $sorted = $sorted->filterBy('category', $category, ',');
    }

    sort($categories);

    // Pagination
    $note = $sorted->paginate(4);
    $pagination = $note->pagination();

    return [
        'categories' => $categories,
        'categoryurl' => $categoryurl,
        'note' => $note,
        'notes' => $notes,
        'pagination' => $pagination,
        'sorted' => $sorted,
        'tags' => $tags
    ];

};

notes.php (templates)

<?php snippet('header') ?>

<main>

    <div class="l-content">

        <div class="o-intro">
            <h1 class="o-intro__title"><?= $page->headline()->or($page->title()) ?></h1>
            <p class="o-intro__description"><?= $page->intro() ?></p>
        </div>

        <?php if (count($categories)): ?>
            <nav class="o-filter">
                <ul class="o-filter__list">
                    <li class="o-filter__item"><a href="<?= $categoryurl ?>" class="o-filter__link">All</a></li>
                    <?php foreach ($categories as $category): ?>
                        <li class="o-filter__item">
                            <a href="<?= url($categoryurl, ['params' => ['category' => urlencode($category)]]) ?>" class="o-filter__link">
                                <?= $category ?>
                            </a>
                        </li>
                    <?php endforeach ?>
                </ul>
            </nav>
        <?php endif ?>

        <div class="o-text">
            <?= $page->editor()->blocks() ?>
        </div>

    </div>

    <div class="o-list">
        <?php foreach ($note as $item): ?>
            <article class="o-note">
                <a href="<?= $item->url() ?>" class="o-note__link">

                    <?php if ($cover = $item->cover()->toFile()): ?>
                        <img src="<?= $cover->thumb(['blur' => 100, 'q' => 10, 'fit' => 'crop', 'fp-x' => $cover->focusX(), 'fp-y' => $cover->focusY()])->url() ?>" data-src="<?= $cover->thumb(['fit' => 'crop', 'fp-x' => $cover->focusX(), 'fp-y' => $cover->focusY()])->url() ?>" alt="<?= $cover->alt() ?>" class="o-note__img j-lazy">
                    <?php endif ?>

                    <div class="o-note__info">

                        <time class="o-note__date"><?= $item->noteDate()->toDate('%d %h %Y') ?></time>

                        <div class="o-note__text">
                            <p class="o-note__category"><?= $item->category() ?></p>
                            <h2 class="o-note__title"><?= $item->headline()->or($item->title())->excerpt(40) ?></h2>
                        </div>

                        <p class="o-note__read"><?= t('readmore') ?></p>

                    </div>

                </a>
            </article>
        <?php endforeach ?>
    </div>

    <?php snippet('pagination') ?>

</main>

<?php snippet('footer') ?>

note.php (controller)

<?php

return function ($page) {

    // Images
    $cover = $page->cover()->toFile();

    // Tags
    $tags = $page->tags()->split(',');
    $tagurl = $page->parent()->url();

    if ($tag = urldecode(param('tag'))) {
        $tags = $page->filterBy('tags', $tag, ',');
    }

    // Related Articles
    $related = page('blog')->children()->listed()->filterBy('category', $page->category(), ',');
    $sorted = $related->sortBy('noteDate', 'desc', 'noteTime', 'desc')->not($page->uri())->limit(2);

    $count = $sorted->count();

    if ($count < 2) {
        $latest = page('blog')->children()->listed()->not($sorted);
        $sortlatest = $latest->sortBy('noteDate', 'desc', 'noteTime', 'desc');
        $sorted = $sorted->add($sortlatest)->not($page->uri())->limit(2);
    }

    return [
        'cover' => $cover,
        'related' => $related,
        'sorted' => $sorted,
        'tags' => $tags,
        'tagurl' => $tagurl
    ];

};

note.php (templates)

<?php snippet('header') ?>

<main>

    <div class="l-content">

        <div class="o-intro">
            <h1 class="o-intro__title"><?= $page->headline()->or($page->title()) ?></h1>
            <p class="o-intro__description"><?= $page->intro() ?></p>
        </div>

        <?php if (count($tags)): ?>
            <nav class="o-filter">
                <ul class="o-filter__list">
                    <?php foreach($tags as $tag): ?>
                        <li class="o-filter__item">
                            <a href="<?= url($tagurl, ['params' => ['tag' => urlencode($tag)]]) ?>" class="o-filter__link">
                                <?= $tag ?>
                            </a>
                        </li>
                    <?php endforeach ?>
                </ul>
            </nav>
        <?php endif ?>

    </div>

    <?php if ($cover): ?>
        <figure class="o-cover">
            <img src="<?= $cover->thumb(['blur' => 100, 'q' => 10, 'fit' => 'crop', 'fp-x' => $cover->focusX(), 'fp-y' => $cover->focusY()])->url() ?>" data-src="<?= $cover->thumb(['fit' => 'crop', 'fp-x' => $cover->focusX(), 'fp-y' => $cover->focusY()])->url() ?>" alt="<?= $cover->alt() ?>" class="o-cover__img j-lazy">
        </figure>
    <?php endif ?>

    <div class="l-content">

        <div class="o-text">
            <?= $page->editor()->blocks() ?>
        </div>

        <p class="o-author">
            Posted on <time class="o-author__date"><?= $page->noteDate()->toDate('%d %B %Y') ?></time> in <a href="<?= url($page->parent()->url(), ['params' => ['category' => urlencode($page->category())]]) ?>" class="o-filter__link"><?= ucwords($page->category()) ?></a> <?php if ($author = $page->author()->toUser()): ?>by <?= $author->name() ?><?php endif ?>
        </p>

    </div>

    <?php snippet('prevnext') ?>

    <div class="o-list">
        <?php foreach($sorted as $item): ?>
            <article class="o-note">
                <a href="<?= $item->url() ?>" class="o-note__link">

                    <?php if ($cover = $item->cover()->toFile()): ?>
                        <img src="<?= $cover->thumb(['blur' => 100, 'q' => 10, 'fit' => 'crop', 'fp-x' => $cover->focusX(), 'fp-y' => $cover->focusY()])->url() ?>" data-src="<?= $cover->thumb(['fit' => 'crop', 'fp-x' => $cover->focusX(), 'fp-y' => $cover->focusY()])->url() ?>" alt="<?= $cover->alt() ?>" class="o-note__img j-lazy">
                    <?php endif ?>

                    <div class="o-note__info">

                        <time class="o-note__date"><?= $item->noteDate()->toDate('%d %h %Y') ?></time>

                        <div class="o-note__text">
                            <p class="o-note__category"><?= $item->category() ?></p>
                            <h2 class="o-note__title"><?= $item->headline()->or($item->title())->excerpt(40) ?></h2>
                        </div>

                        <p class="o-note__read"><?= t('readmore') ?></p>

                    </div>

                </a>
            </article>
        <?php endforeach ?>
    </div>

</main>

<?php snippet('footer') ?>

prevnext.php (snippet)

Listed above

@texnixe You may need to help me figure out the logic for this custom page method.

You have to pass your sorted collection as an argument-

Oh right, ok. That makes sense to me now xD

It doesn’t output anything.

Have you registered the methods in a plugin or added the complete file in your plugins folder?

Aye, created pagemethods folder inside plugins folder, with the index.php and this code:

<?php

Kirby::plugin('texnixe/pagemethods', [
    'pageMethods' => [

        'getPrev' => function($siblings, $sort = [], $status = false) {

            if($sort) $siblings = call(array($siblings, 'sortBy'), $sort);
            $index = $siblings->indexOf($this);
            if($index === false or $index === 0) return null;
            if($status) {
                $siblings = $siblings->limit($index);
                $siblings = $siblings->{$status}();
                return $siblings->last();
            } else {
                return $siblings->nth($index - 1);
            }

        },

        'getNext' => function($siblings, $sort = [], $status = false) {

            if($sort) $siblings = call([$siblings, 'sortBy'], $sort);
            $index = $siblings->indexOf($this);
            if($index === false) return null;
            if($status) {
                $siblings = $siblings->offset($index+1);
                $siblings = $siblings->{$status}();
                return $siblings->first();
            } else {
                return $siblings->nth($index + 1);
            }

        }

    ]
]);

It works as I can cause errors.

I then added the sorted collection into the note.php controller.

$notes = $page->children()->listed();
$sortnotes = $notes->sortBy('noteDate', 'desc', 'noteTime', 'desc');

And in the prevnext.php snippet I ammended it to be

<?php if ($prev = $page->getPrev($sortnotes)): ?>
            <div class="o-prevnext__prev">
                <a class="o-prevnext__link" href="<?= $prev->url() ?>" rel="prev">
                    <?= $prev->headline()->or($prev->title()) ?> >
                </a>
            </div>
        <?php endif ?>

What did I miss?

Hm, good question. I have to look into that tonight.

Haha, ok cool. Lets see what @jimbobrjames comes back with, if anything.

Is there any reason the documentation for prevListed / nextListed talks about the ability to sort, but in the code have nothing to do with sorting?

No idea, maybe a leftover from the K2 docs.

The path @texnixe is leading you down is the way to go.

1 Like

Hm, I just tested this in a Starterkit like this:

<?php
$articles = page('notes')->children()->listed()->sortBy('title', 'asc'); // change sort order as you want

foreach ($articles as $article) {
  echo $article->title() . '-' . $article->date()->toDate('Y-m-d') . '<br>';
  echo ($p = $article->getPrev($articles)) ? $p->title() . '<br>' : 'no prev<br>';
}
?>

This gives me the right precedent, no matter how I change the sort order.


(Alt: Shows the current page and underneath the precedent page in a collection sorted by title, asc)

Do you pass the $sortnotes variable to your snippet?

1 Like

Hmmm, I just booted it up this morning and it works :S My dev environment must of had enough last night, haha.

Thank you very much for these two page methods. It would be great to see the sort functionality added to the prevListed / nextListed like the documentation says.