Limiting number of custom posts shown on taxonomy page

As I have stated in comments, this will take some work. In short, you will need the following:

  • Get all the terms from the style taxonomy. Here we would only want the term ids

  • Get the current tax_query from the colour taxonomy. Use each term from the style taxonomy and create a new tax_query to get posts from the current page’s term and the term being looped over from the style taxonomy

  • Run a custom query on each term from the style taxonomy to get an array of post ids from each term. Here you would want to add all your custom parameters. We will use the order from this custom queries as post order

  • Pass the array of post ids to the main query. Here you would not want to add custom sorting etc. We will use the sequence of the post ids in the post id array for sorting

FEW NOTES BEFORE THE CODE:

  • The code requires PHP 5.4+ due to the use of short array syntax ([]). You should also not be using any version older than PHP5.4. If you however do, just change the short array syntax to the old array syntax (array()). For example, get_terms( $taxonomy, ['fields' => 'ids'] ) should become get_terms( $taxonomy, array( 'fields' => 'ids' ) )

  • I have build in a transient system to reduce the extra strain coming from all the extra work that we need to do. This transient is set to expire in a week, you can adjust this to be longer or shorter. The transient will be automatically deleted when a new post is published, deleted, undeleted or updated

  • I have commented the code very well so you can follow and make some kind of sense from what I was doing as I went along

  • You can extend and modify as needed. Make sure to check out my comments

  • With this new code, you can scrap my complete idea of sorting as described in my linked answer on SO. The code given in this answer should take care of this sorting. Any adjustments that you would want to make should be done in the custom queries and get_terms calls. Just remember to flush your transients after each modification, or better, uncomment the transient calls and return to normal once you are happy with all your modifications

THE CODE:

As always, we will be using pre_get_posts to alter the main query as needed

add_action( 'pre_get_posts', function ( $q )
{       
    if (    !is_admin() // Targets only front end queries
         && $q->is_main_query() // Targets only main query
         && $q->is_tax( 'colour' ) // Targets only taxonomy pages
    ) {

        /** 
         * To save on the extra work that we need to do to get our results,
         * lets save everything in a transient. We will save the string of post ids
         * in the transient. 
         *
         * We will only delete and recreate this transient when a new post is published,
         * deleted, undeleted or updated. This will save us a lot of extra hard work
         * on every page load
         *
         * @link https://codex.wordpress.org/Transients_API
         */
        $queried_object = get_queried_object(); // Get the current queried object to build a unique transient name
        /**
         * Use md5 to build a unique transient name to avoid any conflicts
         * The code below will create a unique transient name which will look like this
         * "colourtax_1ac37e97ee207e952dfc2b8f7eef110f"
         *
         * Note, this should NOT be longer that 45 characters else the transient will be regenerated
         * on each page load. Transients are expensive to create, so don't want to recreate this on every
         * page load due to a name being to long. As a quick guide the md5 part generate a 32 character string,
         * so the "colourtax_" part should be a maximum of 13 characters
         */
        $unique_transient_name="colourtax_" . md5( $queried_object->taxonomy . $queried_object->slug . $queried_object->term_id );
        if ( false === ( $post_ids_array = get_transient( $unique_transient_name ) ) ) {

            // Gets the current tax_query
            $tax_query = $q->tax_query->queries; 
            // Choose the taxonomy to sort by
            $taxonomy = 'style'; 
            // Set the variable to hold the sorted post ids according to terms
            $post_ids_array = [];

            /**
             * Get all the terms from the selected taxonomy to sort by. Just get term ids
             * Add additional arguments here as needed
             * 
             * @link https://codex.wordpress.org/Function_Reference/get_terms
             */
            $terms = get_terms( $taxonomy, ['fields' => 'ids'] ); 
            if (    $terms // Check if the array has valid terms, not empty
                 && !is_wp_error( $terms ) // Check that we do not have any error
            ) { 
                // Define a variable to hold all post ID
                $posts_ids="";
                foreach ( $terms as $term ) {
                    /**
                     * NOTE: You would want to do everything here
                     *
                     * Build our query args, add all your relevant arguments here
                     * You should extend this to include your custom parameter values
                     * like meta_queries en sorting order.
                     * Do a var_dump( $wp_query ) and use the relevant arguments from
                     * there to make this dynamic
                     */
                    $args = [
                        'post_type' => 'any',
                        'posts_per_page' => 3, // Get only 3 posts per term
                        'fields' => 'ids', // Only get post ids to make query faster and more lean
                        // Build a tax_query to add additional terms from selected taxonomy to sort by  
                        'tax_query' => [ 
                            $tax_query, // Our default tax_query from the taxonomy page
                            [
                                'taxonomy' => $taxonomy,
                                'terms' => $term,
                                'include_children' => false,
                            ],
                        ],
                    ];
                    // Return an array of post ids only
                    $posts_array = get_posts( $args );
                    // First check if we have posts to avoid bugs in our code
                    if ( $posts_array ) {
                        // Break the ids array up into a string for later processing
                        foreach ( $posts_array as $v )
                            $posts_ids .= ' ' . $v;
                        unset( $v );    
                    } //endif $posts_array

                } //endforeach $terms
                unset( $term );

                // ADDITIONAL, CAN DELETE THE FOLLOWING SECTION IF YOU WANT TO. READ COMMENTS BELOW

                /**
                 * You can remove the following section. The idea here is as follow:
                 * Any post without a term in the style taxonomy will not be displayed on
                 * a colour taxonomy term page. To avoid this, we will need to get all posts
                 * that does not have a post in style taxonomy. This posts will be displayed last
                 * on the page
                 *
                 * If you are very sure that all posts are tagged in a colour AND style taxonomy
                 * term, then you can remove this section, this is really just a fall back
                 */
                $args_additional = [
                    'post_type' => 'any',
                    'posts_per_page' => 3, // Get only 3 posts without style taxonomy term, adjust as needed
                    'fields' => 'ids', // Only get post ids to make query faster and more lean
                    // Build a tax_query to get posts that is not tagged in style taxonomy  
                    'tax_query' => [ 
                        $tax_query, // Our default tax_query from the taxonomy page
                        [
                            'taxonomy' => $taxonomy,
                            'terms' => $terms,
                            'include_children' => false,
                            'operator' => 'NOT IN', // Posts should not have these terms from style taxonomy
                        ],
                    ],
                ];
                // Return an array of post ids only
                $posts_array_2 = get_posts( $args_additional );
                // First check if we have posts to avoid bugs in our code
                if ( $posts_array_2 ) {
                    // Break the ids array up into a string for later processing
                    foreach ( $posts_array_2 as $v )
                        $posts_ids .= ' ' . $v;
                    unset( $v );    
                } //endif $posts_array

                // STOP DELETING HERE!!

                // Create an array of post ids from the $posts_ids string
                $post_ids_array = explode( ' ', ltrim( $posts_ids ) );

            } //endif $terms

            /**
             * Set the transient if it does not exist. 
             * NOTE: We will choose a week for expiry date, set as needed
             *
             * @link https://codex.wordpress.org/Transients_API#Using_Time_Constants
             */     
            set_transient( $unique_transient_name, $post_ids_array, 7 * DAY_IN_SECONDS );   
        } // endif transient check

        /**
         * Check if we have an array of post ID's before changing anything on the tax page
         *
         * Here we will alter the main query. You do not want to add or remove anything
         * here. Any custom parameters like sorting should be done in the custom queries
         * above
         *
         * DO NOT CHANGE ANYTHING IN THE CODE BELOW EXCEPT posts_per_page
         */
        if ( !empty( $post_ids_array ) ) {
            $q->set( 'post__in', $post_ids_array ); // Posts to get as set in our array, max of 3 posts per term
            $q->set( 'orderby', 'post_in' ); // Sort our posts in the order it is passed in the post__in array
            $q->set( 'order', 'ASC' );
            $q->set( 'posts_per_page', -1 ); // You can change this, if I remember, you need all posts on one page
        }

    } //endif conditional checks for query
});

This will take care of everything except the flushing of the transients on post publish, delete, etc

The following code will take care of this. Everytime a post is updated, published, trashed or untrashed, the transition_post_status hook fires, so we will use that logic to delete all our transients which contains part of the colourtax_ name

add_action( 'transition_post_status', function ()
{
    global $wpdb;
    $wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE ('_transient%_colourtax_%')" );
    $wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE ('_transient_timeout%_colourtax_%')" );
});

Leave a Comment