picocms / Pico

Pico is a stupidly simple, blazing fast, flat file CMS.
http://picocms.org/
MIT License
3.81k stars 616 forks source link

Show total pages from a specific template #638

Closed ctuxboy closed 2 years ago

ctuxboy commented 2 years ago

In twig there is a filter for showing the number of pages:

{{ pages|length }}

Shows the total for ALL the pages.

Is it also possible in Pico count the pages with a specific template? Try this, but doesn't work:

{{ pages("TEMPLATE")|length }}

I think it is possible with a for loop, but is not very efficient, i mean not good for performance.

digitalinferno commented 2 years ago

The length filter returns the number of items of a sequence or mapping, or the length of a string.

So {{ "number"|length }} return 6 and {{ pages("yoursubdir")|length }} return the number of pages in your subdir.

ctuxboy commented 2 years ago

The length filter returns the number of items of a sequence or mapping, or the length of a string.

So {{ "number"|length }} return 6 and {{ pages("yoursubdir")|length }} return the number of pages in your subdir.

I read the docs about the pages()~function. The problem is that the pages with the specific templates exist in different dirs, so it not only in one dir.

/province-1/city-1/location-1.md /province-1/city-1/location-2.md ... /province-1/city-2/location-1.md ... /province-2/city-1/location-1.md ... /province-x/city-x/location-x.md

I want thΓ© total numbers of pages with template:'location'

ctuxboy commented 2 years ago

Now i do it this way:

{% set count = 0 %}
{% for page in pages %}
  {%- if page.meta["template"] == 'location' %}
    {% set count = count +1 %}
  {% endif -%}
{% endfor %}
Total locations:  {{ count }}

This works perfect! Next step is adding this in a macro for reuse for showing total cities :smiley:

I'm afraid, the only issue is the performace, i have several for-loops on some pages, with 500+ pages i'm affraid, maybe it speeds down the website. Someone knows if it's possible caching variables in Twig?

digitalinferno commented 2 years ago

page in pages is not recommended for sites with many pages, page in pages() is faster

{% set loc = '' %}
{% for page in pages("",offset=3) %}
    {% set this = page.id|split("/")|slice(1, 1)|join %}
    {% if this starts with 'location' %} {% set loc = loc + 1 %}{% endif %}
{% endfor %}
{{ loc }}

Change the offset for the right content.

But why not a meta tag location?

in your .md add:

---
location: true
---

and in twig:

{% set loc = '' %}
{% for page in pages("",offset=3) %}
    {% if page.meta.location %} {% set loc = loc + 1 %}{% endif %}
{% endfor %}
{{ loc }}
ctuxboy commented 2 years ago

@vodkafilm thanks for the 'golden' tip :smiley: Fast reply... Okay i try this evening or tomorrow. I use this metadata in the md-files for the location (in my project is the template-name 'dogzone' instead of 'location', but using 'location' looks more default, and maybe better if it can help others):

---
template: location
---

Is your solution location: true a better solution or it doesn't matter?

Anyway i try using your suggestion with the pages()-function if it gives better performance :+1:

Here you can see what i try to rebuild... Screenshot 2022-05-14 11 50 19

And that's the original website: dogvalley.be

digitalinferno commented 2 years ago

It all depends on the type of information you want to process. If you need to know only if the .md file is dedicated to a location the logic is true / false.

location: true {% if page.meta.location %}this is a location{% endif %} this is a location

If you also need to know what kind of location then you can add more info like:

location: city {% if page.meta.location %}this is a {{ page.meta.location}} location{% endif %} this is a city location

ctuxboy commented 2 years ago

Ah okay, Thanks for the info. I using three templates province.twig, city.twig and location.twig, and also a default-template, for other pages. So that is the reason i using template: province/city or location :smiley:

Okay, tested your code (with a little customizing, remove the if-statement), it works perfect :blush:

{% set loc = '' %}
{% for page in pages("",offset=4) %} {# offset 4 => /../province/city/location #}
  {% set loc = loc + 1 %}
{% endfor %}

Total locations: {{ loc }}
digitalinferno commented 2 years ago

Your code is right, but the if is more future-proof because if you add some others type of .md file you are sure to count only the pages with the right meta tag.

mayamcdougall commented 2 years ago

Sorry, been away for a couple days, so I didn't get a chance to comment here before.

Unfortunately, as far as I know, you've indeed run into a limitation of Twig. Sometimes there's just no way to get the data you want without iterating. And with a large number of pages too, this ends up being a worst case scenario. πŸ˜”

pages() is indeed faster than pages... but that's because it allows you to limit the scope of what you're looking through. If you're still iterating over a large number of pages, it might not be much faster.

The only really quick way to do this counting would probably be to write a plugin for it (I can't really help with that though, sorry. πŸ˜…).

But ultimately, you'll have to just try it and see what the performance is like. "Slow" is always a relative thing too... as we're usually still talking about a couple MS of delay. In the end, the small performance impact might not actually matter.

ctuxboy commented 2 years ago

Your code is right, but the if is more future-proof because if you add some others type of .md file you are sure to count only the pages with the right meta tag.

Ah okay, a 'double check'. Good tip!

ctuxboy commented 2 years ago

@mayamcdougall , Hmmm develop a plugin? It's to difficult at this (beginning) stage for myself :wink: , focus now building the theme. I will test it with a lot of pages, and indeed the speed is 'relative', if it slow down the website <= 1sec, this is not a 'worst case', there are a lot of websites loads in more than 5sec :smile:

PhrozenByte commented 2 years ago

I don't really have to add much to the discussion here, just to clear this up: A plugin won't be any faster. Plugins aren't any faster unless they actually do something else to accomplish the same. Unless you choose to change your directory structure this is indeed the best solution possible.

mayamcdougall commented 2 years ago

@PhrozenByte Interesting. Good to know. πŸ€”

You're always quick to suggest running complex operations in a plugin rather than Twig, so I guess I just assumed something could be done a little quicker (or smarter) using PHP directly (eg, even if you're still stuck iterating over everything, at least you're doing it directly in PHP, not through the extra layer of Twig code being converted to PHP operations).

My bad. Shouldn't have assumed. πŸ˜…

digitalinferno commented 2 years ago

@mayamcdougall I think a plugin should be a solution for common task, meanwhile a twig code should be specific for the theme template

PhrozenByte commented 2 years ago

Since Twig still is pretty limited on what you can do with it, you can often implement things differently with PHP - things get faster not because it's written in PHP, but due to other - faster - algorithms. If you end up doing exactly the same (iterating all pages to count pages matching some criteria) it won't really be any faster.

mayamcdougall commented 2 years ago

I think a plugin should be a solution for common task, meanwhile a twig code should be specific for the theme template

Agreed. But if there were a way it could help, it could have been worth doing.

things get faster not because it's written in PHP, but due to other - faster - algorithms.

Yeah, that makes sense. I wasn't actually thinking it through when I suggested it. πŸ€·πŸ»β€β™€οΈ πŸ˜…

ctuxboy commented 2 years ago

Thanks for all the explanation. I try it soon build this option with php.