Support » Developing with WordPress » Posts Per Page -> Per Post Type

  • Hi all,
    Sorry if this has been covered, been googling for a while.
    I have a query that contains 3 custom post types (‘news’, ‘exhibitors’, ‘speakers’).
    I would like to display a total of 6 posts per page and 2 custom post types each, so two news, two exhibitors and two speakers.
    Currently if I specify ‘posts_per_page’ => 6′ and there is only 1 news article, 1, exhibitor there will be 4 speakers shown.
    I almost need a ‘posts_per_post_type’ if that makes sense.
    Query below, many thanks in advance.

    $args = array( 'post_type' => array('news', 'exhibitors', 'speakers'), 'posts_per_page' => 6, 'order' => 'DESC', 'orderby' => 'type',  'paged' => $paged, 'tax_query' => array(
                            array(
                                'taxonomy' => 'foebar',
                                'field' => 'slug',
                                'terms' => array( $post_slug ) 
                            )
                        ));
    • This topic was modified 3 years, 4 months ago by Ainsley Clark.
Viewing 15 replies - 31 through 45 (of 62 total)
  • Yes, that is what figure outs the page count.

    first change this line,

    
    $sqlQuerys['found_post_query'] = str_replace("#sql_col#", 'count(primary_wp_posts.id)', $sqlQuerys['found_post_query']);
    
    to
    
    $sqlQuerys['found_post_query'] = str_replace("#sql_col#", 'count(primary_wp_posts.id),foe_342fj29x2_posts.post_type', $sqlQuerys['found_post_query']);
    

    Then add the dump line after the query.

    
    $page_count_per_post_type = $wpdb->get_results($sqlQuerys['found_post_query'], ARRAY_N);
    
    // dump the results
    echo "<pre>";
    var_dump($page_count_per_post_type);
    echo "</pre>";
    

    This should show you a list of the number posts in each type per post type, where you can see if any single count looks off. Each would be filtered based on the post_type, taxonomy of ‘foobar’, slug of ‘$post_slug’, and a post_status of ‘publish’.

    The results should look something like this, but with your post types.

    
    array(3) {
      [0]=>
      array(2) {
        [0]=>
        string(1) "5"
        [1]=>
        string(5) "post1"
      }
      [1]=>
      array(2) {
        [0]=>
        string(1) "8"
        [1]=>
        string(5) "post2"
      }
      [2]=>
      array(2) {
        [0]=>
        string(1) "5"
        [1]=>
        string(5) "post3"
      }
    }
    

    We can dig in to the query, but its using a sql based count – so it is returning a count based on the number of records in the database that match the criteria.

    I will look at figuring out a way to find the last post of a type.

    ok, to get the last page for each type.

    Add $selectFoundPostQueryAddon, after $whereTemplateAddon in the foreach

    
    $selectFoundPostQueryAddon[] = "WHEN primary_wp_posts.post_type = '$post_type' THEN post_type_$post_type_key.grouped_id";
    

    Add $found_post_query_select_addon, after the $sqlQuerys

    
    // Assemble Queries
    		$sqlQuerys['found_post_query'] = $sql_template;
    		$sqlQuerys['wp_query'] = $sql_template;
    
    		$found_post_query_select_addon = "CASE " . implode(' ', $selectFoundPostQueryAddon). " ELSE null END as post_ids";
    

    update the $sqlQuerys[‘found_post_query’] line

    
    $sqlQuerys['found_post_query'] = str_replace("#sql_col#", 'count(primary_wp_posts.id), primary_wp_posts.post_type,'.$found_post_query_select_addon, $sqlQuerys['found_post_query']);
    

    add $last_post_id_by_type to the paging code to extract the last post id

    
    // get the largest page count on a page
    		$largest_page_count_per_post_type = 0;
    		$found_posts = 0;
    		$last_post_id_by_type= [];
    		foreach ($page_count_per_post_type as $page_count) {
    		    $largest_page_count_per_post_type = ($page_count[0] > $largest_page_count_per_post_type)? $page_count[0]:$largest_page_count_per_post_type;
    		    $found_posts += $page_count[0];
    
    		   // get last post id for each type
    		    $tmp = explode(',', $page_count[2]);
    		    $last_post_id_by_type[$page_count[1]] = (int) end($tmp);
    		}
    

    Then for the if statement.

    
    if ($post_type == 'news') {
     if (isset($last_post_id_by_type[$post_type]) && $last_post_id_by_type[$post_type] == $post->ID) {
      //do something 
     }
    }
    
    • This reply was modified 3 years, 3 months ago by vrandom. Reason: removed bold from code statement, as it was ignoring the html tag
    Thread Starter Ainsley Clark

    (@ainsleyclark)

    Hi @vrandom

    I have used the var_dump to get the results, its below. This is for a total of 8, 4 news, 3 exhibitors and 1 speaker.

    array(3) {
      [0]=>
      array(3) {
        [0]=>
        string(2) "69"
        [1]=>
        string(10) "exhibitors"
        [2]=>
        string(10) "175,121,52"
      }
      [1]=>
      array(3) {
        [0]=>
        string(2) "31"
        [1]=>
        string(4) "news"
        [2]=>
        string(15) "398,397,392,343"
      }
      [2]=>
      array(3) {
        [0]=>
        string(2) "30"
        [1]=>
        string(8) "speakers"
        [2]=>
        string(2) "32"
      }
    }

    With regards to the last post count per page (of post type). I have popped your code in, but only works on some of the post types. I have printed the page_count array in the for loop and it looks liek the page number has something to do with it.
    I have included it below:

    // get the largest page count on a page
    $largest_page_count_per_post_type = 0;
    $found_posts = 0;
    $last_post_id_by_type= [];
    foreach ($page_count_per_post_type as $page_count) {
        $largest_page_count_per_post_type = ($page_count[0] > $largest_page_count_per_post_type)? $page_count[0]:$largest_page_count_per_post_type;
        $found_posts += $page_count[0];
    
       // get last post id for each type
        $tmp = explode(',', $page_count[2]);
        $last_post_id_by_type[$page_count[1]] = (int) end($tmp);
    
        echo '<h1>'.  print_r($page_count) . '</h1>';
    }

    SQL Querys:

        $sqlQuerys['found_post_query'] = str_replace("#sql_col#", 'count(foe_342fj29x2_posts.id), foe_342fj29x2_posts.post_type,'.$found_post_query_select_addon, $sqlQuerys['found_post_query']);
        //$sqlQuerys['found_post_query'] = str_replace("#sql_col#", 'count(foe_342fj29x2_posts.id),foe_342fj29x2_posts.post_type', $sqlQuerys['found_post_query']);
        $sqlQuerys['found_post_query'] = str_replace("#append#", 'group by foe_342fj29x2_posts.post_type', $sqlQuerys['found_post_query']);
        $sqlQuerys['found_post_query'] = str_replace("#where_addon_$post_type_key#", '', $sqlQuerys['found_post_query']);
        $sqlQuerys['found_post_query'] = str_replace("#join_templates_$post_type_key#", str_replace("#sql_offset#", 0,  $joinTemplate[$post_type_key]), $sqlQuerys['found_post_query']);
        $sqlQuerys['found_post_query'] = str_replace("#where_$post_type_key#", $whereTemplate[$post_type_key], $sqlQuerys['found_post_query']);
    
        $sqlQuerys['wp_query'] = str_replace("#sql_col#", '*', $sqlQuerys['wp_query']);
        $sqlQuerys['wp_query'] = str_replace("#append#", "", $sqlQuerys['wp_query']);
        $sqlQuerys['wp_query'] = str_replace("#where_addon_$post_type_key#", $whereTemplateAddon[$post_type_key], $sqlQuerys['wp_query']);
        $sqlQuerys['wp_query'] = str_replace("#join_templates_$post_type_key#", str_replace("#sql_offset#", $sql_offset,  $joinTemplate[$post_type_key]), $sqlQuerys['wp_query']);
        $sqlQuerys['wp_query'] = str_replace("#where_$post_type_key#", $whereTemplate[$post_type_key], $sqlQuerys['wp_query']);

    Assemble Queries:

    // Assemble Queries
    $sqlQuerys['found_post_query'] = $sql_template;
    $sqlQuerys['wp_query'] = $sql_template;
    $found_post_query_select_addon = "CASE " . implode(' ', $selectFoundPostQueryAddon). " ELSE null END as post_ids";

    Addon:

    $selectFoundPostQueryAddon[] = "WHEN foe_342fj29x2_posts.post_type = '$post_type' THEN post_type_$post_type_key.grouped_id";

    Conditional:

                echo '<h1>' .$post->ID. '</h1>';
                if (isset($last_post_id_by_type[$post_type]) && $last_post_id_by_type[$post_type] == $post->ID) {
                    echo "<h1>Last Post</h1>";
                }
    Thread Starter Ainsley Clark

    (@ainsleyclark)

    @vrandom
    The last post count works if there’s only one post type in that page.

    Humm,

    I assume your talking about the $last_post_id_by_type[$post_type]

    
    foreach ($page_count_per_post_type as $page_count) { ... }
    // after for each add this 
    
    echo "<pre>";
    var_dump($last_post_id_by_type);
    echo "<pre>";
    

    It should return an array.

    
    array(3) {
      ["exhibitors"]=>
      int(52)
      ["news"]=>
      int(343)
      ["speakers"]=>
      int(32)
    }
    

    for debug at the conditional, lets show the last id for each post type. And verify what the post type is returning.

    
    echo '<h1>' .post_type . - . .$post->ID. '</h1>';
    if (isset($last_post_id_by_type[$post_type])) {
     echo "<h1>{$last_post_id_by_type[$post_type]}</h1>";
    }
    

    Another thing to look at is the post->id as you got to each page. It should match the order in the $page_count in the for each.

    
    array(3) {
      [0]=>
      array(3) {
        [0]=>
        string(2) "69"
        [1]=>
        string(10) "exhibitors"
        [2]=>
        string(10) "175,121,52"
      }
      [1]=>
      array(3) {
        [0]=>
        string(2) "31"
        [1]=>
        string(4) "news"
        [2]=>
        string(15) "398,397,392,343"
      }
      [2]=>
      array(3) {
        [0]=>
        string(2) "30"
        [1]=>
        string(8) "speakers"
        [2]=>
        string(2) "32"
      }
    }
    

    So on page 1, the page type exhibitors should list out post id 175 and 121. On page 2, it should only show post id 52 (which is the also the last post id)

    Unless there is something changing the order by it should be consistent.

    Thread Starter Ainsley Clark

    (@ainsleyclark)

    @vrandom

    Ive spotted two problems:
    The last post is always for the very last post, regardless of the page.
    The post types arent in order. For example:

    array(3) {
      [0]=>
      array(3) {
        [0]=>
        string(2) "69"
        [1]=>
        string(10) "exhibitors"
        [2]=>
        string(10) "175,121,52"
      }
      [1]=>
      array(3) {
        [0]=>
        string(2) "31"
        [1]=>
        string(4) "news"
        [2]=>
        string(15) "398,397,392,343"
      }
      [2]=>
      array(3) {
        [0]=>
        string(2) "30"
        [1]=>
        string(8) "speakers"
        [2]=>
        string(2) "32"
      }
    }
    array(3) { ["exhibitors"]=> int(52) ["news"]=> int(343) ["speakers"]=> int(32) }

    Spits out
    1: Post ID – 397
    2: Post ID – 398
    And on the second page:
    3: Post ID – 343
    4: Post ID – 392

    On every page this:

    if (isset($last_post_id_by_type[$post_type])) {
       echo "<h1>{$last_post_id_by_type[$post_type]}</h1>";
    }

    Returns 343 (The last in the array)

    Which Im presuming why the one speaker works because there’s only one of them.

    Is there any work around with the pagination too?

    Thank you for your patience, most would have given up by now!

    ok, i see what is happening. Odd that it didnt happen on my dev site but who knows why.

    After the second query, there is some code that reorders the $loop->post as it was not grouping the post type next to each other and that caused some weird display issues.

    I have updated the code to use the sql order to rebuild the array.

    
    // get the largest page count on a page
    		$largest_page_count_per_post_type = 0;
    		$found_posts = 0;
    		$last_post_id_by_type= [];
    		$post_order_index= [];
    		foreach ($page_count_per_post_type as $page_count) {
    		    $largest_page_count_per_post_type = ($page_count[0] > $largest_page_count_per_post_type)? $page_count[0]:$largest_page_count_per_post_type;
    		    $found_posts += $page_count[0];
    
    		    // set up post order index
    		    $post_order_index[$page_count[1]] = explode(',', $page_count[2]);
    
    		    // get last post id for each type
    		    $last_post_id_by_type[$page_count[1]] = (int) end($post_order_index[$page_count[1]]);
    
    		}
    

    then down the page,

    
    // put the post in the order of the post_types array
    		if ($loop->have_posts()) {
    
    		    // make posts index by post_type
    		    $tmpPosts = [];
                foreach ($loop->posts as $k => $v) {
                    $tmpPosts[get_post_type( $v->ID )][$v->ID] = $v;
                }
    
                // assemble new ordered posts
                $finPosts = [];
                foreach ($post_types as $k => $v) {
                    if (isset($tmpPosts[$v])) {
                        // reorder based on the sql result order
                        foreach ($post_order_index[$v] as $k1 => $v1) {
                            if (isset($tmpPosts[$v][$v1])) {
                                $finPosts[] = $tmpPosts[$v][$v1];
                            }
                        }
                    }
                }
    
                // update the $loop with the new ordered posts
                $loop->posts = $finPosts;
    		}
    

    “The last post is always for the very last post, regardless of the page.”

    I thought that was what you wanted, but i think you want the last post of each type for the current page.

    Remove the $last_post_id_by_type from the $page_count_per_post_type foreach

    
    		    // get last post id for each type
    		    $last_post_id_by_type[$page_count[1]] = (int) end($post_order_index[$page_count[1]]);		
    

    Update the “// assemble new ordered posts” code from above

    
     // assemble new ordered posts
                $finPosts = [];
                foreach ($post_types as $k => $v) {
                    if (isset($tmpPosts[$v])) {
    
                        // reorder based on the sql result order
                        foreach ($post_order_index[$v] as $k1 => $v1) {
                            if (isset($tmpPosts[$v][$v1])) {
                                $finPosts[] = $tmpPosts[$v][$v1];
    
                                // set last post id for each type
    		            $last_post_id_by_type[$v] = (int) $v1;
                            }
                        }
                    }
                }
    

    The last_post_id_by_type[post_type] should now contain the last post id from the current page.

    • This reply was modified 3 years, 3 months ago by vrandom. Reason: typo-o
    Thread Starter Ainsley Clark

    (@ainsleyclark)

    @vrandom You absolute legend,

    Thank you so much! Apologies if I wasn’t clear.

    All working now.

    Have you any suggestions on the pagination? Just need the prev and next page, with the correct amount of pages.

    Can we take:

      array(3) {
        [0]=>
        string(2) "31"
        [1]=>
        string(4) "news"
        [2]=>
        string(15) "398,397,392,343"
      }

    And count of these and manually pass it into where the pagination is counted?

    Ok, to hide the page numbers. Since we are using a custom paginate_links function we can add a new option.

    in the function, paginate_links_with_provided_wpquery, add the wrapping if (if (! $args[‘hide_page_numbers’]) {) statement. The rest is just for you to be able to find where.

    
    if (! $args['hide_page_numbers']) {
                    for ( $n = 1; $n <= $total; $n++ ) :
                        if ( $n == $current ) :
                            $page_links[] = "<span aria-current='" . esc_attr( $args['aria_current'] ) . "' class='page-numbers current'>" . $args['before_page_number'] . number_format_i18n( $n ) . $args['after_page_number'] . "</span>";
                            $dots = true;
                        else :
                            if ( $args['show_all'] || ( $n <= $end_size || ( $current && $n >= $current - $mid_size && $n <= $current + $mid_size ) || $n > $total - $end_size ) ) :
                                $link = str_replace( '%_%', 1 == $n ? '' : $args['format'], $args['base'] );
                                $link = str_replace( '%#%', $n, $link );
                                if ( $add_args )
                                    $link = add_query_arg( $add_args, $link );
                                $link .= $args['add_fragment'];
    
                                /** This filter is documented in wp-includes/general-template.php */
                                $page_links[] = "<a class='page-numbers' href='" . esc_url( apply_filters( 'paginate_links', $link ) ) . "'>" . $args['before_page_number'] . number_format_i18n( $n ) . $args['after_page_number'] . "</a>";
                                $dots = true;
                            elseif ( $dots && ! $args['show_all'] ) :
                                $page_links[] = '<span class="page-numbers dots">' . __( '&hellip;' ) . '</span>';
                                $dots = false;
                            endif;
                        endif;
                    endfor;
    			}
    

    “And count of these and manually pass it into where the pagination is counted?”

    If you get a count of the posts, it does not calculate the correct number of pages in order to show all the results.

    I will try to explain the page counting. For example, we have the following

    news, 5 posts
    speakers, 8 posts
    exhibitors, 6 posts

    if we add that up, we get 19 posts, now if we calculate the number of pages, ceil( total posts / number per page )

    So that is, ceil( 19 / 6 ), which is 3 – so 3 pages

    But since we are showing 2 per post type to total 6, it does not take in to account if we have more of one post type than another to show.

    If we get the number of pages needed to show all the results for each post type

    news, 5 posts – need 3 pages at 2 per page to display results
    speakers, 8 posts – need 4 pages at 2 per page to display results
    exhibitors, 6 posts – need 3 pages at 2 per page to display results

    So if we go with the total 19 posts at 6 per page it said we only needed 3 pages, but speakers needs 4. So the speakers would lose 2 posts as it needed one more page to display all its results.

    Hope that makes sense.

    Thread Starter Ainsley Clark

    (@ainsleyclark)

    Hi @vrandom

    It does make a lot of sense.

    With the code above, where shall I put this under paginate_links_with_provided_wpquery? Not entirely sure…

    Yes that code block is in the paginate_links_with_provided_wpquery function.

    Then down in the main code, update the settings (add hide_page_numbers to the array) for the paginate function call.

    
    // Previous/next page navigation.
    $args = array(
    	'prev_text' => __( 'Previous page', 'twentysixteen' ),
    	'next_text'          => __( 'Next page', 'twentysixteen' ),
    	'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'twentysixteen' ) . ' </span>',
    	'screen_reader_text' => __( 'Posts navigation' ),
    	'type'               => 'plain',
    	'hide_page_numbers'          => true
    );
    
    Thread Starter Ainsley Clark

    (@ainsleyclark)

    @vrandom Amazing amazing.

    So the only thing is its returning 1 more page than it should.
    Can I do a -1 somewhere?! I know its probably not that simple!

    Also, if this is a complete pain – dont worry. But if there is say 10 news stories and only 2 speakers and 2 exhibitors, it would create 5 pages.
    Is there a way to populate that second page with news stories? So in turn it would create 3 pages.

    Page 1
    2 News
    2 Speakers
    2 Exhibitors

    Page 2
    6 Exhibitors

    Page 3
    2 News

    Thank you so much!

    Humm, not sure why it would be calculating the pages off by one. db count sql are usually accurate

    
    $page_count_per_post_type = $wpdb->get_results($sqlQuerys['found_post_query'], ARRAY_N);
    
    // add these lines
    echo "<pre>";
            var_dump($page_count_per_post_type[0][1] . ': ' . ceil( $page_count_per_post_type[0][0] / 2 ));
            var_dump($page_count_per_post_type[1][1] . ': ' . ceil( $page_count_per_post_type[1][0] / 2 ));
            var_dump($page_count_per_post_type[2][1] . ': ' . ceil( $page_count_per_post_type[2][0] / 2 ));
            echo "</pre>";
    

    this should dump out the calculated pages per post type. The highest one should be the number of pages displayed. If its not then someplace in the code is changing it.

    For your second question.

    The db query is build to only fetch 2 per post type. I dont see a way to do what you ask, as it would mess up the query results.

    for example, if a post type is empty, then grab more listings from another post type.

    I have been pondering.

    A walk through:

    page 1, it loads (2 news, 2 exhibitors, 2 speakers)
    page 2, it loads (2 news, 1 exhibitors, 2 speakers)
    — we detect that there are not 2 in each post type.
    — we set a tmp var holding the types that are below 2 posts (exhibitors)
    — (#a) check if holding var has all three types
    — if yes, then no more posts to get, goto #b, if no continue next line
    — find lowest page count of post types, if two are the same use the order
    — (news, exhibitors, speakers)
    — in this case, news and speakers have 2 post, exhibitors is excluded
    — news, speakers – in use order, news is selected.
    — we check the $page_count_per_post_type post id list
    — extract the next post id from the list
    — (using sql_offset for type and current posts used count, to find it)
    — if no post id found, then set type in holding var and goto #a
    — if post id is last post and displayed, then set type in holding var, goto #a
    — add the found post id to a “need_to_fetch” var for later
    — add one to the news count in page_count_per_post_type
    — recheck total, have 6 posts to display?
    — no, then go back to #a and repeat for the next available post type
    — #b, if have need_to_fetch post ids, continue next line, if not, goto #c
    — proceed to fetching the posts in the “need_to_fetch” list
    — add results to the original results
    — update custom sql_offset for each type
    — (using posts used this page load + previous sql_offset for each type)
    — store sql_offset in session as we need to send to next page
    — #c
    — done with this part, continue with rest of code

    In walk through, we end up with (3 news, 1 exhibitors, 2 speakers) on page 2.

    At first i didnt think it was possible. Since it would mess up the base sql as it has an order that it follows.

    After that walk thought. I think it could be done. This would revert the pagination to using the total number of posts (add post count from each post type together for total) in the page calculation. It would also prevent the page from only displaying multiple pages of less than 6 on the page.

    We would have to keep a custom offset for each post type in order to maintain what has been used and what has not. This should allow for all pages to have 6 results (maybe not last page) using whatever counts per type needed to get 6 results.

    If this sounds good, i will see if i can get some time to make the code adjustments.

    Rebuilt code – a whole lot has changed.

    It will:
    – fetch up to $post_per_posttype number of posts for each post type specified in the $post_types array
    – if this result is less than (count($post_types) * $post_per_posttype), then it will attempt to get more posts from the other post types.
    – it will use the $post_types order to get new listings

    Let me know if this works for what your wanting.

    Readable copy here, https://gist.github.com/TerrePorter/110facd34376b8ab171890a8bb247081

    
    <?php
        // hack to overwrite the max_num_queries var
    		class WP_Query_CustomSQL extends WP_Query
    		{
    
    			function get_posts() {
    			    $posts = parent::get_posts();
    
    			    // overwrite max_num_pages settings if set
    			    $q = &$this->query_vars;
    
    			    if (isset($q['max_num_pages'])) {
    			        $this->max_num_pages = $q['max_num_pages'];
    			    }
    
    			    return $posts;
                }
    		}
    
    		function paginate_links_with_provided_wpquery( $args = '', $wp_query) {
    			global $wp_rewrite;
    
    			// Setting up default values based on the current URL.
    			$pagenum_link = html_entity_decode( get_pagenum_link() );
    			$url_parts    = explode( '?', $pagenum_link );
    
    			// Get max pages and current page out of the current query, if available.
    			$total   = isset( $wp_query->max_num_pages ) ? $wp_query->max_num_pages : 1;
    			$current = get_query_var( 'paged' ) ? intval( get_query_var( 'paged' ) ) : 1;
    
    			// Append the format placeholder to the base URL.
    			$pagenum_link = trailingslashit( $url_parts[0] ) . '%_%';
    
    			// URL base depends on permalink settings.
    			$format  = $wp_rewrite->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : '';
    			$format .= $wp_rewrite->using_permalinks() ? user_trailingslashit( $wp_rewrite->pagination_base . '/%#%', 'paged' ) : '?paged=%#%';
    
    			$defaults = array(
    				'base'               => $pagenum_link, // http://example.com/all_posts.php%_% : %_% is replaced by format (below)
    				'format'             => $format, // ?page=%#% : %#% is replaced by the page number
    				'total'              => $total,
    				'current'            => $current,
    				'aria_current'       => 'page',
    				'show_all'           => false,
    				'prev_next'          => true,
    				'prev_text'          => __( '&laquo; Previous' ),
    				'next_text'          => __( 'Next &raquo;' ),
    				'end_size'           => 1,
    				'mid_size'           => 2,
    				'type'               => 'plain',
    				'add_args'           => array(), // array of query args to add
    				'add_fragment'       => '',
    				'before_page_number' => '',
    				'after_page_number'  => '',
    				'hide_page_numbers'  => false
    			);
    
    			$args = wp_parse_args( $args, $defaults );
    
    			if ( ! is_array( $args['add_args'] ) ) {
    				$args['add_args'] = array();
    			}
    
    			// Merge additional query vars found in the original URL into 'add_args' array.
    			if ( isset( $url_parts[1] ) ) {
    				// Find the format argument.
    				$format = explode( '?', str_replace( '%_%', $args['format'], $args['base'] ) );
    				$format_query = isset( $format[1] ) ? $format[1] : '';
    				wp_parse_str( $format_query, $format_args );
    
    				// Find the query args of the requested URL.
    				wp_parse_str( $url_parts[1], $url_query_args );
    
    				// Remove the format argument from the array of query arguments, to avoid overwriting custom format.
    				foreach ( $format_args as $format_arg => $format_arg_value ) {
    					unset( $url_query_args[ $format_arg ] );
    				}
    
    				$args['add_args'] = array_merge( $args['add_args'], urlencode_deep( $url_query_args ) );
    			}
    
    			// Who knows what else people pass in $args
    			$total = (int) $args['total'];
    			if ( $total < 2 ) {
    				return;
    			}
    			$current  = (int) $args['current'];
    			$end_size = (int) $args['end_size']; // Out of bounds?  Make it the default.
    			if ( $end_size < 1 ) {
    				$end_size = 1;
    			}
    			$mid_size = (int) $args['mid_size'];
    			if ( $mid_size < 0 ) {
    				$mid_size = 2;
    			}
    			$add_args = $args['add_args'];
    			$r = '';
    			$page_links = array();
    			$dots = false;
    
    			if ( $args['prev_next'] && $current && 1 < $current ) :
    				$link = str_replace( '%_%', 2 == $current ? '' : $args['format'], $args['base'] );
    				$link = str_replace( '%#%', $current - 1, $link );
    				if ( $add_args )
    					$link = add_query_arg( $add_args, $link );
    				$link .= $args['add_fragment'];
    
    				/**
    				 * Filters the paginated links for the given archive pages.
    				 *
    				 * @since 3.0.0
    				 *
    				 * @param string $link The paginated link URL.
    				 */
    				$page_links[] = '<a class="prev page-numbers" href="' . esc_url( apply_filters( 'paginate_links', $link ) ) . '">' . $args['prev_text'] . '</a>';
    			endif;
    			if (! $args['hide_page_numbers']) {
                    for ( $n = 1; $n <= $total; $n++ ) :
                        if ( $n == $current ) :
                            $page_links[] = "<span aria-current='" . esc_attr( $args['aria_current'] ) . "' class='page-numbers current'>" . $args['before_page_number'] . number_format_i18n( $n ) . $args['after_page_number'] . "</span>";
                            $dots = true;
                        else :
                            if ( $args['show_all'] || ( $n <= $end_size || ( $current && $n >= $current - $mid_size && $n <= $current + $mid_size ) || $n > $total - $end_size ) ) :
                                $link = str_replace( '%_%', 1 == $n ? '' : $args['format'], $args['base'] );
                                $link = str_replace( '%#%', $n, $link );
                                if ( $add_args )
                                    $link = add_query_arg( $add_args, $link );
                                $link .= $args['add_fragment'];
    
                                /** This filter is documented in wp-includes/general-template.php */
                                $page_links[] = "<a class='page-numbers' href='" . esc_url( apply_filters( 'paginate_links', $link ) ) . "'>" . $args['before_page_number'] . number_format_i18n( $n ) . $args['after_page_number'] . "</a>";
                                $dots = true;
                            elseif ( $dots && ! $args['show_all'] ) :
                                $page_links[] = '<span class="page-numbers dots">' . __( '&hellip;' ) . '</span>';
                                $dots = false;
                            endif;
                        endif;
                    endfor;
    			}
    			if ( $args['prev_next'] && $current && $current < $total ) :
    				$link = str_replace( '%_%', $args['format'], $args['base'] );
    				$link = str_replace( '%#%', $current + 1, $link );
    				if ( $add_args )
    					$link = add_query_arg( $add_args, $link );
    				$link .= $args['add_fragment'];
    
    				/** This filter is documented in wp-includes/general-template.php */
    				$page_links[] = '<a class="next page-numbers" href="' . esc_url( apply_filters( 'paginate_links', $link ) ) . '">' . $args['next_text'] . '</a>';
    			endif;
    			switch ( $args['type'] ) {
    				case 'array' :
    					return $page_links;
    
    				case 'list' :
    					$r .= "<ul class='page-numbers'>\n\t<li>";
    					$r .= join("</li>\n\t<li>", $page_links);
    					$r .= "</li>\n</ul>\n";
    					break;
    
    				default :
    					$r = join("\n", $page_links);
    					break;
    			}
    			return $r;
    		}
            // ----------------------------------------------------
    
            // set up vars
    		$post_slug='news'; //$post->post_name;
            $post_types = ['post1', 'post2', 'post3'];
    
            // number of post to show per each post_type
            $post_per_posttype = 2;
    
            // to keep the last post id that is being displayed on the page
            $last_post_id_by_type= [];
    
            // keeps the display of posts order
            $post_order_index= [];
    
            // get the current page number
            $current_page_number = get_query_var('paged', 1);
            // if 0, then we are on page 1
            if ($current_page_number==0) { $current_page_number = 1; }
    
            // check if we have sql_offset info for this page load
            $transient_key = implode('-', $post_types).'_sqloffset_page';
            if ( false === ( $sql_offset = get_transient( $transient_key . $current_page_number) ) ) {
                // no valid transient set
                foreach ($post_types as $post_type_key => $post_type) {
                    $sql_offset[$post_type] = 0;
                }
            }
    
    		// Make SQL Parts
            $joinTemplate = [];
            $whereTemplate = [];
    		foreach ($post_types as $post_type_key => $post_type) {
    
    		    $joinTemplate[] = "left join 			 
                    (select GROUP_CONCAT(id  order by post_date desc, id desc) as grouped_id, post_type from (
                          SELECT id, post_type, post_date
                          FROM wp_posts 
                            inner join wp_term_relationships on wp_term_relationships.object_id = id
                            inner join wp_term_taxonomy on wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
                            inner join wp_terms on wp_term_taxonomy.term_id = wp_terms.term_id
                            where wp_term_taxonomy.taxonomy = 'category' and wp_terms.slug = '$post_slug'
                           and post_type in ('$post_type')
                           and post_status = 'publish'
                           order by post_date desc, id desc                       
                          ) d$post_type_key
                         GROUP BY d$post_type_key.post_type) post_type_$post_type_key		 
                         ON wp_posts.post_type = post_type_$post_type_key.post_type";
    
    		    $selectFoundPostQueryAddon[] = "WHEN wp_posts.post_type = '$post_type' THEN post_type_$post_type_key.grouped_id";
    		}
    
    		// base tempate for sql query
    		$sql_template = "select count(wp_posts.id), wp_posts.post_type#sql_col# from wp_posts #join_templates# where wp_posts.post_type in ('".implode("', '", $post_types)."') group by wp_posts.post_type";
    
            // Assemble Query
    
    		// adds a comma seperated list of the post ids that are found in each join statement to the results
    		$found_post_query_select_addon = "CASE " . implode(' ', $selectFoundPostQueryAddon). " ELSE null END as post_ids";
    		$sql_template = str_replace("#sql_col#", ','.$found_post_query_select_addon, $sql_template);
    
    		// adds generated joins to query
    		$sql_template = str_replace("#join_templates#", implode("\n", $joinTemplate), $sql_template);
    
            // execute posts build query
    		global $wpdb;
    		$page_count_per_post_type = $wpdb->get_results($sql_template, ARRAY_N);
    
            // set vars for found_posts, max_posts_count and build the post_order_index
    		$found_posts = 0;
    		$max_posts_count=0;
    		foreach ($page_count_per_post_type as $page_count) {
    
    		    // make a total post found for all post_types
    		    $found_posts += $page_count[0];
    
    		    // max posts - total of posts per type
    		    $max_posts_count += $page_count[0];
    
    		    // set up post order index
    		    $post_order_index[$page_count[1]] = explode(',', $page_count[2]);
    
    		}
    
    		// set the max pages for the results
    		$max_posts_num_pages =  ceil( $max_posts_count / ($post_per_posttype * count($post_types)));
    
            // get base post ids to show using the normal post_per_posttype
    		$no_more_post_for_post_type=[];
    		$post_ids_to_fetch = [];
    		foreach ($post_order_index as $k => $v) {
    
                // using the sql_offset for this post type, get the next x results from the post_order_index
                $t = array_slice($v, $sql_offset[$k], $post_per_posttype);
    
                // if number available is less than or equal to the offset+post_per_posttype, then there are no more post to fetch for this type
                if (count($v) <=  ($sql_offset[$k] + $post_per_posttype)) {
                    $no_more_post_for_post_type[] = $k;
                }
    
                // keep a growing list of post ids
    		    $post_ids_to_fetch = $post_ids_to_fetch + array_combine($t, array_pad([], count($t), $k));
    		}
    
    		// if we need more posts to make total wanted on page
    	    while (count($post_ids_to_fetch) < (count($post_types) * $post_per_posttype)) {
    
    	        // get current count of postids to fetch by post type
    		    $post_ids_to_fetch_count_per_type = array_count_values($post_ids_to_fetch);
    
    		    // remove any post_types that do not have any more posts
    		    $post_types_with_more_posts = [];
    		    foreach ($post_types as $k => $v) {
    		        if (!in_array($v, $no_more_post_for_post_type)) {
    		            $post_types_with_more_posts[] =$v;
    		        } else {
    		            unset($post_ids_to_fetch_count_per_type[$v]);
    		        }
    		    }
    
    		    // if have more posts
    		    if (count($post_types_with_more_posts)) {
    
    		        // get the post type with the lowest count from the number of ids in $post_ids_to_fetch
                    $fetch_more_from_post_type = array_search(min($post_ids_to_fetch_count_per_type), $post_ids_to_fetch_count_per_type);
    
                    // get the next post id from the post order index
                    $t = array_slice($post_order_index[$fetch_more_from_post_type], $sql_offset[$fetch_more_from_post_type] + $post_ids_to_fetch_count_per_type[$fetch_more_from_post_type], 1);
    
                    // check if this is the last id available
                    if ((count($post_order_index[$fetch_more_from_post_type]) <=  ($sql_offset[$fetch_more_from_post_type] + $post_ids_to_fetch_count_per_type[$fetch_more_from_post_type] + 1))) {
                        $no_more_post_for_post_type[] = $fetch_more_from_post_type;
                    }
    
                    $post_ids_to_fetch = $post_ids_to_fetch + array_combine($t, array_pad([], count($t), $fetch_more_from_post_type));
    
    		    } else {
    		        //There are no more posts to fetch, stop trying to get more
    		        break; // exit the while loop
    		    }
    
    		}
    
    		// get current count of postids to fetch by post type
    		$post_ids_to_fetch_count_per_type = array_count_values($post_ids_to_fetch);
    
    		// fetch the display posts query
    		$loop = new WP_Query_CustomSQL(
    		        array(
    		                'post_type'      => $post_types,
                            'post__in'      => array_keys($post_ids_to_fetch),
    		                'max_num_pages' => $max_posts_num_pages
    		             )
    		        );
    
     		// put the post in the order of the post_types array
    		if ($loop->have_posts()) {
    
    		    // make posts index by post_type
    		    $tmpPosts = [];
                foreach ($loop->posts as $k => $v) {
                    $tmpPosts[get_post_type( $v->ID )][$v->ID] = $v;
    
                    // update the offset to add in this post to the count
                    $sql_offset[get_post_type( $v->ID )] += 1;
                }
    
                // assemble new ordered posts
                $finPosts = [];
                foreach ($post_types as $k => $v) {
                    if (isset($tmpPosts[$v])) {
    
                        // reorder based on the sql result order
                        foreach ($post_order_index[$v] as $k1 => $v1) {
                            if (isset($tmpPosts[$v][$v1])) {
    
                                // make an array of posts grouping them in the post_types order
                                $finPosts[] = $tmpPosts[$v][$v1];
    
                                // set last post id for each type
    		                    $last_post_id_by_type[$v] = (int) $v1;
                            }
                        }
                    }
                }
    
                // update the $loop with the new ordered posts
                $loop->posts = $finPosts;
    
                // save the sql offset data for the next page view
                if ($current_page_number + 1 <= $max_posts_num_pages) {
                    set_transient($transient_key . ($current_page_number + 1), $sql_offset);
                }
    		}
    
    		// begin display code
    
    		$speakerCounter = 0;
    		$exhibitorCounter = 0;
    		$columnwidth = 'col-lg-6';
    		if ( $loop->have_posts() ) :
    			while ( $loop->have_posts() ) : $loop->the_post();
    
    		        // get post type of current post
    				$post_type = get_post_type( $post->ID );
    
    				/// do something with the last post of each post_type being displayed on the page
                    if ($last_post_id_by_type[$post_type] == $post->ID) {
                          //do something
                          echo "LAST POST";
                    }
    
    				if ($post_type == 'post1' && $newsCounter < $post_ids_to_fetch_count_per_type['post1']) {
                        $newsCounter++;
                        //require( locate_template ('blocks/content-newsrow.php'));
                        echo $post->ID . '] ' . $post_type . ' - '  .  get_permalink($post->ID)  . "<BR>";
                    }
                    if ($post_type == 'post2' && $exhibitorCounter < $post_ids_to_fetch_count_per_type['post2']) {
                        if ($exhibitorCounter == 0) {
                            echo '<div class="row"><div class="col-12 mb-2 mb-lg-3">'; ?>
                            <span class="contentbrands__title type__weight--medium">Exhibitors for <?php echo $contentbrand; ?>:</span>
                            </div></div>
                            <?php echo '<div class="row">'; ?>
                        <?php }
                            $exhibitorCounter++;
                            //require( locate_template ('blocks/content-exhibitor.php'));
                            echo $post->ID . '] ' . $post_type . ' - '  .  get_permalink($post->ID)  . "<BR>";
                        if ($exhibitorCounter == 2) {echo '</div>';}
                    }
                    if ($post_type == 'post3' && $speakerCounter < $post_ids_to_fetch_count_per_type['post3']) {
                        if ($speakerCounter == 0) {
                            echo '<div class="row"><div class="col-12 mb-2 mb-lg-3">'; ?>
                            <span class="contentbrands__title type__weight--medium">Experts for <?php echo $contentbrand; ?>:</span>
                            </div></div>
                            <?php echo '<div class="row">'; ?>
                        <?php }
                            $speakerCounter++;
                            //require( locate_template ('blocks/content-speaker.php'));
                            echo $post->ID . '] ' . $post_type . ' - '  .  get_permalink($post->ID)  . "<BR>";
                        if ($speakerCounter == 1) {echo '</div>';}
                    }
    
    			endwhile;
    
    			// Previous/next page navigation.
    			$args = array(
    				'prev_text'          => __( 'Previous page', 'twentysixteen' ),
    				'next_text'          => __( 'Next page', 'twentysixteen' ),
    				'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'twentysixteen' ) . ' </span>',
    				'screen_reader_text' => __( 'Posts navigation' ),
    				'type'               => 'plain',
    				'hide_page_numbers'          => true
    			);
    
    			// Set up paginated links.
    			$links = paginate_links_with_provided_wpquery( $args , $loop);
    
    			if ( $links ) {
    				echo _navigation_markup( $links, 'pagination', $args['screen_reader_text'] );
    			}
    			?>
    
    			<?php
    		else :
    			echo '<h3>No News</h3>';
    		endif;
    		wp_reset_postdata();
    
    
    Thread Starter Ainsley Clark

    (@ainsleyclark)

    Thank you very much @vrandom for all of this. Will take a look tomorrow.

Viewing 15 replies - 31 through 45 (of 62 total)
  • The topic ‘Posts Per Page -> Per Post Type’ is closed to new replies.