I use Timber for Wordpress and I would like to create a dictionary system like that :
https://wordpress.stackexchange.com/questions/119163/displaying-custom-post-type-by-first-letter-through-custom-taxonomy
I wrote this :
lexique.php
$query = get_posts(array('post_type' => 'lexique','posts_per_page' => -1));
But I don't know how to transform this with Timber :
$by_letter = array();
while( $query->have_posts() ) { $query->the_post();
global $post;
$letter = substr($post->post_name, 0, 1);
if ( ! isset($by_letter[$letter]) ) $by_letter[$letter] = array();
$by_letter[$letter][] = $post;
}
wp_reset_postdata();
It’s a good idea to get all posts at once. By adding the orderby
parameter, you can already put them into the correct order for the letter-sorting. I’d use title
and not name
for the sorting and to define the first letter, because name
/post_name
is a URL-safe string (used in permalinks) might be different from the actual title of the post.
You get the posts through Timber::get_posts()
, so that you don’t have to rely on The Loop. You get an array with posts that you can work with, before you render it through a Twig file. This is much more direct than the method you linked to, because you don’t have to rely on additional functions and query resets.
lexique.php
$posts = Timber::get_posts( array(
'post_type' => 'lexique',
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
) );
$posts_by_letter = array();
// Sort posts by letter
foreach ( $posts as $post ) {
$first_letter = substr( $post->post_title, 0, 1);
// Create array for letter if it doesn’t exist
if ( ! isset( $posts_by_letter[ $first_letter ] ) ) {
$posts_by_letter[ $first_letter ] = array();
}
$posts_by_letter[ $first_letter ][] = $post;
}
$context['posts_by_letter'] = $posts_by_letter;
Timber::render( [ 'lexique.twig' ], $context );
Display only letters for existing posts
lexique.twig
<dl>
{% for letter, posts in posts_by_letter %}
<dt>{{ letter }}</dt>
{% for post in posts %}
<dd><a href="{{ post.link }}">{{ post.title }}</a></dd>
{% endfor %}
{% endfor %}
</dl>
Because you have a nested array, you have to do two for-loops. The first loop goes through the letters (which are the keys of the outer array). The value assigned to a letter key is another array, containing all the posts starting with that letter. To display the post titles as links, you use the second for-loop.
Display all letters from A to Z
If you want to generate a list of all letters from A to Z and display existing posts, you can use range
and check if posts exists for that letter in posts_by_letter
.
Additionally you can use range
to create a list of anchor links that lets a visitor jump to a specific letter.
lexique.twig
{# Anchor links to jump to letter #}
{% for letter in range('A', 'Z') %}
<a href="#{{ letter }}">{{ letter }}</a>
{% endfor %}
<dl>
{% for letter in range('A', 'Z') %}
<dt><a id="{{ letter }}">{{ letter }}</a></dt>
{% if posts_by_letter[letter] is defined %}
{% for post in posts_by_letter[letter] %}
<dd><a href="{{ post.link }}">{{ post.title }}</a></dd>
{% endfor %}
{% endif %}
{% endfor %}
</dl>