0

I noticed that if I code a custom WP_Query, sticky posts don't show first in the list. So I was wandering how to achieve that. I've almost done it with 3 subsequent queries, but I guess there must be a more elegant solution for this.

One thing I thinked involved at least 2 query:

  • first build a simple function to reuse just to output html

  • second make a query of only stiky posts, and output the html (according to a preset max items)

  • third, is there's still room for new items according to max items, make a query excluding the sticky posts, and again, output html.

So, it all would start like this (and then go on..):

            $max_items = 5;
        $sticky_ids = get_option('sticky_posts');

        if(count($sticky_ids) != 0){
            //query degli sticky
            $sticky_soon_query = new WP_Query();
            $sticky_soon_query->query(array(
                'category_name' => 'coming-soon',
                'posts_per_page' => $max_items,
                'post__in' => $sticky_ids
            ));
        } 

Is there any more elegant solution?

3 Answers 3

2

Further upgrade: just built a class to place in functions.php or maybe in a plugin. I did that mainly because I want almost always have sticky posts, probably in more loops per page. So got a lot of duplication. Therefore here is a basic class, that certainly can be exapanded/perfectioned some way, but for my needs just works:

class Posts_With_Sticky{

public $max_items_amount;
public $normal_items_amount;
public $category;
public $sticky_ids;
public $sticky_query;
public $normal_query;


function __construct($category = null, $max_items_amount = 5){

    $this->max_items_amount = $max_items_amount;
    $this->sticky_ids = get_option('sticky_posts');
    $this->category = $category;

    $this->get_query();
}

public function get_query(){

    //sticky query
    if(count($this->sticky_ids) != 0){
        $this->sticky_query = new WP_Query();
        $this->sticky_query->query(array(
            'category_name' => $this->category,
            'posts_per_page' => $this->max_items_amount,
            'post__in' => $this->sticky_ids
        ));
    }

    //check amount of stikies and room for normals
    if(isset($this->sticky_query)){
        $stikies = count($this->sticky_query->posts);
        if($stikies == $this->max_items_amount) return;
        else $this->normal_items_amount = $this->max_items_amount - $stikies;
    }

    //normal posts query
    $this->normal_query = new WP_Query();
    $this->normal_query->query(array(
        'category_name' => $this->category,
        'posts_per_page' => $this->normal_items_amount,
        'post__not_in' => $this->sticky_ids
    ));

}//end get_query

public function render_posts($sticky_loop_function, 
                                      $normal_loop_function = "", 
                                      $before_markup = "", 
                                      $after_markup = ""){

    if(!$sticky_loop_function) return;

    //shortcut
    $sticky_setted = isset($this->sticky_query);
    $normal_setted = isset($this->normal_query);    

    if(!$sticky_setted && !$normal_setted) return;

    $sticky_loop = $sticky_loop_function;
    $normal_loop = ($normal_loop_function !== "") ? $normal_loop_function : $sticky_loop_function;

    echo $before_markup;

    if($sticky_setted) $sticky_loop($this->sticky_query);
    if($normal_setted) $normal_loop($this->normal_query);

    echo $after_markup;
}//end render_posts

}//end Posts_With_Sticky

Then, in the html page, I'll write a different loop function for every section, and then pass it to render_posts().

Something like this:

        <?php

    //html output function
    function write_soon_items($query){

        while($query->have_posts()){
            $query->the_post();
            $date = get_the_date('d.m.Y');
            $title = get_the_title();
            $excerpt = get_the_excerpt();
            $link = get_permalink();

            echo <<< ITEM_BLOCK
            <article>
                <h4>
                    $date - $title
                </h4>
                <p>
                    <a href="$link" title="$title">$excerpt</a>
                </p>
                <div class="float-reset"></div>
            </article>
ITEM_BLOCK;
        }
    }//end write_sub_items


    $soon_posts = new Posts_With_Sticky('coming-soon');

            $soon_posts->render_posts('write_soon_items')
    ?>

So, every time I need a loop that honours sticky posts of a certain category, I just make another Posts_With_Sticky instance and then call its render_posts() method passing a custom output loop function.

Note that you can pass render_posts() a function for stickies, a function for normals (if you don't, the first one will be used), a 'before_markup' and a 'after_markup'.

Hope it helps someone.

Bye

1

I found a logic error when calculating the amount of normal post in relation to stickies. This is the code that worked for me. @Stratboy, thank you very much for sharing. Cheers!

<?php

//html output function
function write_sub_items($query){

    while($query->have_posts()){
        $query->the_post();
        $date = get_the_date('d.m.Y');
        $title = get_the_title();
        $excerpt = get_the_excerpt();

        echo <<< ITEM_BLOCK
        <li>
            <h4>
                $date - $title
            </h4>
            <p>
                $excerpt
            </p>
        </li>
ITEM_BLOCK;
    }
}//end write_sub_items


    $max_items_amount = 5;
    $normal_items_amount = $max_items_amount;

    $sticky_ids = get_option('sticky_posts');

    //sticky query
    if(count($sticky_ids) != 0){
        $sticky_soon_query = new WP_Query();
        $sticky_soon_query->query(array(
            'category_name' => 'coming-soon',
            'posts_per_page' => 5,
            'post__in' => $sticky_ids
        ));
    }

    //check amount of stikies and room for normals
    if(isset($sticky_soon_query)){
        $stikies = count($sticky_soon_query->posts);
        if($stikies <= $max_items_amount) {
            $normal_items_amount = $max_items_amount - $stikies;
        }
    }

    //normal posts query
    if($normal_items_amount > 0){
        $normal_soon_query = new WP_Query();
        $normal_soon_query->query(array(
            'category_name' => 'coming-soon',
            'posts_per_page' => $normal_items_amount,
            'post__not_in' => $sticky_ids
        ));
    }

    //shortcut
    $stiky_setted = isset($sticky_soon_query);
    $normal_setted = isset($normal_soon_query);

?>

<?php if($stiky_setted || $normal_setted) : ?>
<ul>
    <?php if($stiky_setted){ write_sub_items($sticky_soon_query); } ?>
    <?php if($normal_setted){ write_sub_items($normal_soon_query); } ?>
</ul>
<?php endif; ?>
0

Ok, no answers. Anyway, just to share, for now my (working) solution is something like this:

        <?php

    //html output function
    function write_sub_items($query){

        while($query->have_posts()){
            $query->the_post();
            $date = get_the_date('d.m.Y');
            $title = get_the_title();
            $excerpt = get_the_excerpt();

            echo <<< ITEM_BLOCK
            <li>
                <h4>
                    $date - $title
                </h4>
                <p>
                    $excerpt
                </p>
            </li>
ITEM_BLOCK;
        }
    }//end write_sub_items


        $max_items_amount = 5;
        $normal_items_amount = $max_items_amount;

        $sticky_ids = get_option('sticky_posts');

        //sticky query
        if(count($sticky_ids) != 0){
            $sticky_soon_query = new WP_Query();
            $sticky_soon_query->query(array(
                'category_name' => 'coming-soon',
                'posts_per_page' => 5,
                'post__in' => $sticky_ids
            ));
        }

        //check amount of stikies and room for normals
        if(isset($sticky_soon_query)){
            $stikies = count($sticky_soon_query->posts);
            if($stikies == $max_items_amount) return;
            else $normal_items_amount = $max_items_amount - $stikies;
        }

        //normal posts query
        $normal_soon_query = new WP_Query();
        $normal_soon_query->query(array(
            'category_name' => 'coming-soon',
            'posts_per_page' => $normal_items_amount,
            'post__not_in' => $sticky_ids
        ));

        //shortcut
        $stiky_setted = isset($sticky_soon_query);
        $normal_setted = isset($normal_soon_query);

    ?>

    <?php if($stiky_setted || $normal_setted) : ?>
    <ul>
        <?php if($stiky_setted){ write_sub_items($sticky_soon_query); } ?>
        <?php if($normal_setted){ write_sub_items($normal_soon_query); } ?>
    </ul>
    <?php endif; ?>

The 'soon' word in vars names is nothing important. Here, in my project, I'm writing code for 'coming soon' news, that's all.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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