Timber Wordpress - Show blocks of posts from two categories

北战南征 提交于 2021-02-08 10:20:31

问题


I've created a new page-test.php with the following.

$query = array('category_name' => 'blog, portfolio');
$context['posts'] = Timber::get_posts($query);

This shows posts from just these categories which is great but I want to group these into specific divs on the page. At present I can't get my custom page or tease twig files (I've no idea if I need both or just one) to override the default twig pages. I make changes that either break the page or seem to do nothing.

I'm sure this is totally wrong.

{% extends "base.twig" %}

{% block content %}
    {% for post in posts %}
        <div class="blog">
            {% include ['tease-'~post.post_type~'.twig', 'tease.twig'] %}
        </div>
    {% endfor %}

    {% for post in posts %}
        <div class="portfolio">
            {% include ['tease-'~post.post_type~'.twig', 'tease.twig'] %}
        </div>
    {% endfor %}
{% endblock %}

I've no idea how I make the specific twig files show just the content I want.

Thanks in advance for any help.

Please note I've previously used Expression Engine, Statamic and Craft, so am pretty familiar with this type of thing (not php though) but for some reason this has beaten me.


回答1:


Why your first approach didn’t work

When you get posts for two categories, you get a mix of posts, sorted by date, which is what you got on your first try. You looped over all the posts, once in a div with class blog and once in a div with class portfolio. You did not add a check whether a post was actually in the category blog or portfolio.

As I understand it, you wanted to use post.post_type in your include to discern between the categories. However, post_type is not the category, it will be "post" for your posts and "page" for your pages (or any other name when you use Custom Post Types). Your include tried to find tease-post.twig, which probably didn’t exist.

So the only difference between this:

{% for post in posts %}
    <div class="blog">
        {% include ['tease-'~post.post_type~'.twig', 'tease.twig'] %}
    </div>
{% endfor %}

and this:

{% for post in posts %}
    <div class="portfolio">
        {% include ['tease-'~post.post_type~'.twig', 'tease.twig'] %}
    </div>
{% endfor %}

is in the end only the class name on the div ;).

Now to get the category for a post, you can access it through post.category in your twig file.

{% for post in posts %}
    <div class="blog">
        {% include ['tease-' ~ post.category.slug ~ '.twig', 'tease.twig'] %}
    </div>
{% endfor %}

But then you’d still have posts from the portfolio category in your blog div.

How you could do it

The beauty in Timber is, that you can work on your posts before you handle them over to your template. You can also take the posts that you get from Timber::get_posts() and split them, filter them, extend them, whatever you want.

Now we could sort them by category before handing them over to the template.

One way to do this would be to pass an additional parameter in the query. But for the sake of learning Timber we will take another approach.

We will make a new array with sub-arrays for each category. Let’s call that new array $sorted_posts. We then loop over all the posts, get the category for that posts and fill $sorted_posts so that the keys for the sub-arrays are the slugs of the posts first categories. Like this:

$query = array(
    'category_name' => 'blog,portfolio',
);

$posts = Timber::get_posts( $query );

$sorted_posts = array();

foreach ( $posts as $post ) {
    // Get first category of post
    $category = $post->category();

    // Fill post back to sorted_posts
    $sorted_posts[ $category->slug ][] = $post;
}

// Add sorted posts to context
$context['posts'] = $sorted_posts;

A var_dump() on $sorted_posts could look something like the following, with 5 posts in total (3 portfolio posts and 2 blog posts).

$sorted_posts = array (size=2)
    'portfolio' => array (size=3)
        0 => object(Timber\Post)[379]
        1 => object(Timber\Post)[430]
        2 => object(Timber\Post)[375]
    'blog' => array (size=2)
        0 => object(Timber\Post)[378]
        1 => object(Timber\Post)[429]

In your twig file, you then can just loop over these sub-arrays:

{% extends 'base.twig' %}

{% block content %}

<div class="blog">
    <h3>Blog</h3>
    {% for post in posts.blog %}
        {% include ['tease-blog.twig', 'tease.twig'] %}
    {% endfor %}
</div>

<div class="portfolio">
    <h3>Portfolio</h3>
    {% for post in posts.portfolio %}
        {% include ['tease-portfolio.twig', 'tease.twig'] %}
    {% endfor %}
</div>

{% endblock %}

What this approach won’t handle is when you assign two or more categories to your post.

Why your second solution works

What you did in your second approach was to bypass the post sorting by getting the posts for only one category. This way, you know that in $context['posts'] there will be only posts with category story and in $context['posts2'] there will be only posts with category news. You don’t get a mixed posts array that you’d have to sort first.

Now the good thing is: You probably found the most performant solution for your case, because it uses less database queries than getting the category for each post separately.

When I do this (as in "How you could do it")…

foreach ( $posts as $post ) {
    $category = $post->category();
}

… then WordPress will get the category for each of the posts in an additional database query. I honestly don’t know if there is a possibility to get posts with the category included. If you don’t have many posts that you have to show on your front page, the perceived difference in performance for you or your website visitors is probably negligible.

TL;DR: Good job, stick with the solution you found!




回答2:


So I got this to work, but I'm sure this is a very poor solution.

I added a second query in my page-test.php so posts was Story and posts2 was News.

$query = array('category_name' => 'Story');
$context['posts'] = Timber::get_posts($query);
$query2 = array('category_name' => 'News');
$context['posts2'] = Timber::get_posts($query2);

I then added a second div that referenced the posts2 query and that worked.

{% extends "base.twig" %}

{% block content %}
<div class="blog">
        <h3>Story</h3>
    {% for post in posts %}
        {% include ['tease-story.twig', 'tease.twig'] %}        
    {% endfor %}
    </div>

    <div class="news">
        <h3>News</h3>
    {% for post in posts2 %}
        {% include ['tease-news.twig', 'tease.twig'] %}
    {% endfor %}
    </div>
{% endblock %}

I also created a dedicated 'tease-story.twig' and 'tease-news.twig' as a test but these are not actually needed.

If I'm honest I'm not really sure what I've done here, but it works.

If anyone can help to clarify that would be much appreciated.



来源:https://stackoverflow.com/questions/36292113/timber-wordpress-show-blocks-of-posts-from-two-categories

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!