0

I am trying to write a get_posts() query to retrieve 7 posts per page. The retrieval of these posts are based on 3 different tags, "Featured","Highlighted", and "News". Now the "Featured" post take priority over all others, so if there are 7 Featured Posts then only those post should be displayed on the page. The "Highlighted" posts are second in line when it comes to order of importance. So if there are 2 "Featured" posts and 5 "highlighted" posts that is what should display. Then finally whatever is left over "News" is the final tag designation as far as displaying posts by order of importance.

Here is what I have so far:

public function automate_posts_to_send(){
    $value = get_option( 'email_settings_options');
    $article_number = $value['number_of_articles_to_send'];
    $posts = get_posts( array(
        "tag__in"          => array(135, 239, 256),
        "orderby"           => 'tag__in',
        'date_query'        => array(
                array(
                    'after' => '24 hours ago'
                ),
            ),
        "posts_per_page"    => $article_number
    ) );
    return $posts;
}

I am getting the list but the post order is not right. I want the post ordered this way by these tags: Featured, Highlighted, News

1

It is quite easy to achieve this. Your answer is usort()

FEW NOTES:

Before we code, just a few notes

  • Requires PHP5.4+ due to the use of the short array syntax ([]). If you have an older version, you should really upgrade as older versions are a real security threat as all older versions prior to PHP 5.4 has been EOL'ed

  • I will comment the code as I go along so you can easy follow it

  • Taxonomy terms and custom fields are cached, so you will not be doing extra db calls, so the solution is still very lean.

  • The code below is untested, so be sure to test this locally first

THE CODE:

public function automate_posts_to_send()
{
    $value          = get_option( 'email_settings_options');
    $article_number = $value['number_of_articles_to_send'];

    /**
     * Add the tag ids here in the correct order you need your posts to be in
     * In this example, posts from tag 2 will show first, then posts from tag 3 then tag 1
     * As I said, I'm using short array syntax, so for older versions of PHP prior to 5.4,
     * [2, 3, 1] would become array( 2, 3, 1 )
     */
    $tag_order = [2, 3, 1]; 
    $args = [ 
        'tag__in' => $tag_order,
        //'orderby' => $tag_order // This is invalid ordering, add valid value here according to needs
        'posts_per_page' => $article_number,
        'date_query' => [
            [
                'after' => '24 hours ago'
            ]
        ],
    ];
    $posts_array = get_posts( $args );

    /**
     * Now we will sort the returned array of posts as we need them according to $tag_order. We will use usort()
     *
     * There is a bug in usort causing the following error:
     * usort(): Array was modified by the user comparison function
     * @see https://bugs.php.net/bug.php?id=50688
     * This bug has yet to be fixed, when, no one knows. The only workaround is to suppress the error reporting
     * by using the @ sign before usort
     */
    @usort( $posts_array, function ( $a, $b ) use ( $tag_order )
    {
        // Store our post ids in an array. We will use that to get our post tags per post
        $array = [
            'a' => $a->ID, // Use only post ID
            'b' => $b->ID // Same as above
        ];
        // Define our two variables which will hold the desired tag id
        $array_a = [];
        $array_b = [];

        // We will now get and store our post tags according to the tags in $tag_order
        foreach ( $array as $k=>$v ) {
            $tags = get_the_tags( $v );
            if ( $tags ) {
                foreach ( $tags as $tag ) {
                    if ( !in_array( $tag->term_id, $tag_order ) )
                        continue;

                    // If we found a tag that is in the $tag_order array, store it and break the foreach loop
                    ${'array_' . $k}[] = $tag->term_id; // Will produce $array_a[] = $tag->term_id or $array_b[] = $tag->term_id

                    // If we found the tag we are looking for, break the foreach loop
                    break;
                } // endforeach $tags
            } // endif $tags
        } // endforeach $array

        // Flip the $tag_order array for sorting purposes
        $flipped_tag_order = array_flip( $tag_order );

        /**
         * Lets sort our array now
         *
         * We will sort according to tag first. If the tags between two posts being compared are the same
         * we will sort the posts by post date. Adjust this as necessary
         */
        if ( $flipped_tag_order[$array_a] != $flipped_tag_order[$array_b] ) {
            return $flipped_tag_order[$array_a] - $flipped_tag_order[$array_b];
        } else {
            return $a->post_date < $b->post_date; // Change to > if you need oldest posts first
        }
    }); // end our usort ordering

    // We are done sorting, etc. Return our $posts_array
    return $posts_array;
}
0

tag__in is not listed as a valid orderby value in the codex.

I'd suggest grabbing more posts than you need, and ordering them within your PHP.

  • Why would you want get more posts than what is really necessary. This will impact on performance – Pieter Goosen Aug 26 '15 at 5:18
0

By default, WP doesn't sort by taxonomy/tag. A probable solution is doing 3 separate get_posts calls, one for each tag, then using array_merge to combine the results into 1 array.

  • You can still do it in one query, no need for three :-) – Pieter Goosen Aug 26 '15 at 4:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.