Page prevsibling nextsibling inverted

Hi all, again coming with a stupid strange thing…
My site blogs are perfectly working, organized with numeric prefix.
When serial showing pictures of all post, they all show in a good order O1- 02- etc.
When clicking one, you have prev/next based on some very standard code :

{% if not page.isFirst %}
  <div class="nav-previous" id="navPrevious" pageref="{{ page.prevSibling.url}}"...
{% endif %}
{% if not page.isLast %}
  <div class="nav-next" id="navNext" pageref="{{ page.nextSibling.url}}" ...
{% endif %}

So… Does anyone have any idea why next and prev are inverted ?! What did I touch to have this behavior ? :crazy_face:

thanks for any reply :wink:
.F

Have you had a look at the quark blog-item.html.twig, is slightly different and always good to use quark as a reference point as it is updated / check for compatability for each release.

<p class="prev-next text-center">
{% if not page.isLast %}
        <a class="btn" href="{{ page.prevSibling.url }}"><i class="fa fa-angle-left"></i> {{ 'THEME_QUARK.BLOG.ITEM.PREV_POST'|t }}</a>
{% endif %}

{% if not page.isFirst %}
    <a class="btn" href="{{ page.nextSibling.url }}">{{ 'THEME_QUARK.BLOG.ITEM.NEXT_POST'|t }} <i class="fa fa-angle-right"></i></a>
{% endif %}

What theme are you using where the problem occurs ?

Looks like the THEME_QUARK.BLOG.ITEM.NEXT_POST and THEME_QUARK.BLOG.ITEM.PREV_POST’ might need a bit of explanation, but to this untrained eye is using a variable of where the blog directory lives

THEME_QUARK.BLOG.ITEM.PREV_POST button - Support / Themes & Styling - Grav Community Forum (getgrav.org)

I am sure pamtbaau will be able to tell you straight away, but it looks like where the location of the root of blog lives and query the next posts either side , then sends itself back to the blog (blog-item.html.twig)

as in the example site of the person who put the question in above

<p class="prev-next"><a class="button" href="/seo/ouvrir-url-masse"><i class="fa fa-chevron-left"></i> Article suivant</a>
        
<a class="button" href="/seo/exemple-negative-seo">Article précédent <i class="fa fa-chevron-right"></i></a></p>

from the site Spécialiste SEO (SEO Specialist) | Merci Serguey (mercisergey.com)

(spamhater, you again :yum: thanks for answering. )
So to answer the theme is a one I develop, based on grav skeleton when asking for creating a new one.

The Quark sample you mention is giving the same behavior. clicking the next page sibling will show the previous.
May be I’m a little to dumb to understand by myself, but if I read and translate correctly the code :
if the page is NOT LAST then show a previous button
if the page is NOT FIRST then show a next button

–not last--------------not first –
— <<<<— my page —>>>> ----

isn’t that reversed ?

@Francois, IMHO, I think the behaviour is exactly as expected…

To quote the first hit on Google:

Definition | Meaning of blog:

A blog (a shortened version of “weblog”) is an online journal or informational website displaying information in reverse chronological order, with the latest posts appearing first, at the top. It is a platform where a writer or a group of writers share their views on an individual subject.

So, when showing a single blog item, the Previous is the one written before the current and the Next is the one written after the current.

You are using the Blog related templates designed for a Blog, however, you are not using it as a Blog.

In your use-case, or misuse-case, the way you order the items give a different meaning to Previous and Next. You should therefor adapt the templates to fit your meaning of Previous and Next.

1 Like

@anon76427325 @spamhater
Oh yes of course ! That’s what I forgot, blog is basically ordered by reverse “publication” date and not by numeric name or folder. That’s the point.
So
{% for child in p.collection %} is giving a name ordered,
but
next or prev sibling, the classic blog ordering has formally defined.

And in my case, as all pages where created in a chronologic order, then prev/next seems to be reversed (but is not :slight_smile:).
Thanks for explanations, and you’re right, I will adapt my specific templates.
:trophy:

Just had to deal with siblings and although @anon76427325 explained quite clearly and it makes sense purely for blogs (and similar list), but…

It doesn’t make sense at all in a collection per se.
Please explain me how first item in a collection can have previous sibling or last item - next sibling?
No sense at all :sweat_smile:

well, Idon’t know, may be all collections in grav are treated as blog collections, not just classical list ?

Collections themselves are treated normally. It’s only the sibling search on frontend pages that’s reversed. Instead of + $direction it subtracts - - $direction (the opposite of what’s done in Flex Pages)

I guess the solution would be to switch frontend pages to Flex, but AFAIK it still has issues and not really a viable solution

Actually I passed through this directly in my partial template code. A small (and quiet dirty…) correction in the front end, but sufficient for me.

@Karmalakas, @Francois, Found an old issue from 2015: Collection nextSibling() and prevSibling() are Switched (Reversed).

rhukster commented:

Ok taken me a while to decipher this. I think the problem is simply that we chose a different terminology for previous and next .

In my thinking, next is up the list and previous is down the list.

The logic is debatable, but it is what it is right now.

I honestly don’t see much room to debate logic here :slight_smile: First item shouldn’t have previous sibling as the last one shouldn’t have the next. Also, if you see a list as from left to right (usually) instead of top to bottom, it also all comes together how flawed current approach is :slight_smile: As you noticed prevously,

previous is down the list

makes sense only for blog/article lists where newer is at the top, but not for any other lists :slight_smile:


There was an idea on Discord to have an option to switch the direction, but leave the default as is of course. I think I’ll make a PR some time soon, because I just can’t have such logic (when last still has next) in my code :sweat_smile:

1 Like

@Karmalakas, Here is a suggestion:

Blog template is printing the following:

{% for p in page.collection %}
  <div>
    <div>{{ p.prevSibling.title }}</div>
    <div>{{ p.title }}</div>
    <div>{{ p.nextSibling.title }}</div>
  </div>
{% endfor %}

Default behaviour:

blog.md:

content:
  items: "@self.children"
  order:
    by: folder
    dir: asc

Output:
Untitled

Reversed:

Small update of /system/src/Grav/Common/Page/Collection.php::adjacentSibling()

public function adjacentSibling($path, $direction = 1)
{
  $values = array_keys($this->items);
  $keys = array_flip($values);

  // $reversePrevNext: True only when explicitly set to boolean true. Default false.
  $reversePrevNext = isset($this->params['reversePrevNext']) 
    ? $this->params['reversePrevNext'] === true 
    : false;

  if (array_key_exists($path, $keys)) {
    $index = $keys[$path] + ($reversePrevNext ? $direction : -$direction);

    return isset($values[$index]) ? $this->offsetGet($values[$index]) : $this;
  }

  return $this;
}

blog.md

content:
  items: "@self.children"
  order:
    by: folder
    dir: asc
  reversePrevNext: true

Output:
Untitled

1 Like

Yes, exactly this, but I’d like this param to be available site wide too. There are lots of web pages which don’t even have date ordered content (mine is one of them) and it would be a pain to have this on every page that lists something :slight_smile: I was thinking maybe a setting in the theme config (current themes would behave as usual, but some themes could have the reverse sibling check on every template where collection does not come from frontmatter). Of course your approach should be available too

It’s been brought up many times, and the main reason it has never been changed is legacy. If the logic was reversed from what it is now, and has been since before 2015, it would not be backwards-compatible with virtually any sites, themes, and plugins. And option to switch it doesn’t help much, it’d only confuse what logic happens when and why.

The better alternative would be new nomenclature that captures this “alternative”, by many considered clearer, logic. Most themes won’t generally separate a list of blog posts/articles from any other list of pages, so the generic approach is more suited.

A chart explaining the new nomenclature and methods, and how they differ from nextSibling and previousSibling would help too.

@OleVik, what do you mean by new nomenclature? Because the method names would do exactly what they say. I think new names (eg. something like getNextSibling()) would bring even more confusion :thinking:

IMO having a theme setting which is used to define how these two methods work, would be a good approach. Old themes would not break, all would be as it is now, but new themes could have this setting and getting adjacent pages in front-end would work as expected

The concept of next and previous sibling is already established, and has been in use for years, following the previously linked to methodology (#531#issuecomment-175309487) by Pamtbaau. Adding getter-prefixes doesn’t solve it, as those by convention should be aliases.

A theme-setting for one specific theme wouldn’t be a problem, but if applied generally the whole extension-ecosystem would be divided in how next and previous sibling are implemented. I think it would be more useful to have helper-methods in \Grav\Common\Utils and \Grav\Common\Page that utilize adjacentSibling() to create the opposite of the current sibling-nomenclature.

For both Collections and Pages - which Flex should adapt to, it would be logically consistent to have a next()-method going down the explicit or implicit Collection, and a previous()-method going up. Thus not altering the current sibling-logic, but moving towards a replacement and clarification in the API.

The note in the Docs hasn’t really solved the confusion on this one.

I give up… Tried to figure out, but prev() and next() are already taken because of already implemented \Iterator and \ArrayCollection both on flex and non-flex pages collections.

Also in Twig neither colleection.prev() nor collection.next() work. prev() always returns false or an array which only contains a slug of the page (['slug' => 'page-slug']) and next() always returns null.

Although both prev() and next() moves the pointer and then current() gives the correct page, but there’s a problem - no way to set a pointer to a current page

I really don’t have time to figure out why the implementation of prev() and next() is as it is, but IMO this really requires a solution, so that we could get items we expect and not in reverse :frowning:


OK… Is there any chance such method would be approved in system/src/Grav/Common/Page/Collection.php?

    /**
     * Set current page.
     */
    public function setCurrent(string $path): void
    {
        reset($this->items);

        while (key($this->items) !== $path && key($this->items) !== null) {
            next($this->items);
        }
    }

Then in Twig I could do this:

    {% set collection = page.parent.collection({items: '@self.children', filter: {published: true, routable: true}}) %}
    {% set isFirst = collection.isFirst(page.path) %}
    {% set isLast = collection.isLast(page.path) %}

    <section>
        {% if not isFirst %}
            {% do collection.setCurrent(page.path) or collection.prev %}
            <a href="{{ collection.current.url }}">Previous page</a>
        {% endif %}

        {% if not isLast %}
            {% do collection.setCurrent(page.path) or collection.next %}
            <a href="{{ collection.current.url }}">Next page</a>
        {% endif %}
    </section>

Although I don’t get why prev() and next() would have completely different implementations :thinking: I think this also should be fixed in Grav 2.0 - all these methods should behave as in PHP. At least that’s what I would expect. At the very least, that prev/next would return same type values

In your theme folder, create the following series of sub-folders:
/custom/templates/partials/
Change the file called blog_item.html.twig as it follows:

<div class="list-item h-entry">

    {% if truncate %}
        {% set image_file = page.header.image.summary.enabled|defined(false) ? (page.header.image.summary.file ?: true) %}
    {% else %}
        {% set image_file = page.header.image.text.enabled|defined(false) ? (page.header.image.text.file ?: true) %}
    {% endif %}

    <div class="list-blog-header">
        {% if image_file %}
            {% set image = image_file is same as(true) ? page.media.images|first : (image_file ? page.media.images[image_file]) %}
            {% if image %}
                {{ image.cropZoom(page.header.image.width|defined(900), page.header.image.height|defined(300)).html|raw }}
            {% endif %}
        {% endif %}
        {% if truncate %}
        {% if page.header.link %}
            <h2 class="p-name">
                {% if page.header.continue_link is not same as(false) %}
                <a href="{{ page.url }}"><i class="fa fa-angle-double-right u-url" aria-hidden="true"></i></a>
                {% endif %}
                <a href="{{ page.header.link }}" class="u-url">{{ page.title }}</a>
            </h2>
        {% else %}
            <h2 class="p-name"><a href="{{ page.url }}" class="u-url">{{ page.title }}</a></h2>
        {% endif %}
        {% else %}
            <h2 class="p-name">{{ page.title }}</h2>
        {% endif %}



        {% if page.taxonomy.tag or show_date  %}
        <div class="list-blog-meta">
            {% if show_date %}
            <i class="fa fa-clock-o" aria-hidden="true"></i>
            <time class="dt-published" datetime="{{ page.date|date("c") }}">
                {{ page.date|date('jS M Y') }}
            </time>
            {% endif %}
            {% if page.taxonomy.tag %} <span class="separator">/</span> {% endif %}
            {% if page.taxonomy.tag %}
            <i class="fa fa-tags" aria-hidden="true"></i>
            <span class="tags">
                {% for tag in page.taxonomy.tag %}
                <a href="{{ blog.url|rtrim('/') }}/tag{{ config.system.param_sep }}{{ tag }}" class="p-category">{{ tag }}</a>{% if not loop.last %}, {% endif %}
                {% endfor %}
            </span>
            {% endif %}
        </div>
        {% endif %}
    </div>

    <div class="list-blog-padding">

    {% if page.header.continue_link is same as(false) %}
        <div class="e-content">
            {{ page.content|raw }}
        </div>
        {% if not truncate %}
        {% set show_prev_next = true %}
        {% endif %}
    {% elseif truncate and page.summary != page.content %}
        <div class="p-summary e-content">
            {{ page.summary|raw }}
            <p><a class="read-more button" href="{{ page.url }}">{{ display.read_more.label|default('GANTRY5_ENGINE_READMORE'|trans) }}</a></p>
        </div>
    {% elseif truncate %}
        <div class="p-summary e-content">
            {% if page.summary != page.content %}
                    {{ page.content|truncate(550)|raw }}
            {% else %}
                    {{ page.content|raw }}

            {% endif %}
            <p><a class="read-more button" href="{{ page.url }}">{{ display.read_more.label|default('GANTRY5_ENGINE_READMORE'|trans) }}</a></p>
        </div>
    {% else %}
        <div class="e-content">
            {{ page.content|raw }}
        </div>

        {% if config.plugins.comments.enabled %}
            {% include 'partials/comments.html.twig' %}
        {% endif %}

        {% set show_prev_next = true %}
    {% endif %}

    {% if show_prev_next %}

        <p class="prev-next">
            {% if not page.isFirst %}
                <a class="button" href="{{ page.nextSibling.url }}"><i class="fa fa-chevron-left" aria-hidden="true"></i> {{ 'GANTRY5_ENGINE_BLOG_ITEM_PREV_POST'|trans }}</a>
            {% endif %}

            {% if not page.isLast %}
                <a class="button" href="{{ page.prevSibling.url }}">{{ 'GANTRY5_ENGINE_BLOG_ITEM_NEXT_POST'|trans }} <i class="fa fa-chevron-right" aria-hidden="true"></i></a>
            {% endif %}
        </p>
    {% endif %}


    </div>
</div>

This PR was merged and changes should be available in the upcoming Grav v1.7.19. See PR description how I used this change in my template to have non-inversed pagination