Skip Navigation

[Resolved] Ordering a parametric search view by taxonomy

This thread is resolved. Here is a description of the problem and solution.

Problem:
For a View which queries posts how to order the results by taxonomy.

Solution:
It is a limitation of WordPress that posts cannot be ordered by taxonomy.

To do so requires manually re-ordering the posts with PHP after they have been returned from the database using the wpv_filter_query_post_process filter.

The client provided their own such solution below (https://toolset.com/forums/topic/ordering-a-parametric-search-view-by-taxonomy/#post-617416).

Relevant Documentation:
https://toolset.com/documentation/programmer-reference/views-filters/#wpv_filter_query_post_process

This support ticket is created 6 years, 9 months ago. There's a good chance that you are reading advice that it now obsolete.

This is the technical support forum for Toolset - a suite of plugins for developing WordPress sites without writing PHP.

Everyone can read this forum, but only Toolset clients can post in it. Toolset support works 6 days per week, 19 hours per day.

No supporters are available to work today on Toolset forum. Feel free to create tickets and we will handle it as soon as we are online. Thank you for your understanding.

Sun Mon Tue Wed Thu Fri Sat
- 7:00 – 14:00 7:00 – 14:00 7:00 – 14:00 7:00 – 14:00 7:00 – 14:00 -
- 15:00 – 16:00 15:00 – 16:00 15:00 – 16:00 15:00 – 16:00 15:00 – 16:00 -

Supporter timezone: Europe/London (GMT+00:00)

This topic contains 10 replies, has 3 voices.

Last updated by jonathanB-19 6 years, 9 months ago.

Assisted by: Nigel.

Author
Posts
#617265

Hello,

I've created a custom post type 'Machine' and 3 custom taxonomies to categorize it:
- machine brand
- machine type
- machine business segment

I've chosen to use taxonomies instead of custom post fields for two reasons:

1 - It's easy to build a parametric search view of Machine posts, with filters for the related taxonomies and displaying only filter options with valid results for all the combined filters.

2 - My client will need to manage machine brands, types and business segment lists.
Taxonomies seem an easier interface for my client to manage than using custom post fields (which seem more fit to predefined and fixed lists and to be managed by the developer) or an alternative with hierarchical custom post types in which machine brands, types and business segments would be custom post types themselves, having cpt 'Machine' as their parent - which I've tried but also found not so simple for the client to manage.

The question is that the client expects this kind of ordering on the Machines view outcome (and considering that the view starts up with no filter selected):

> First order by the post's taxonomy 'machine brand'
> then order by the post's title or eventually by the post's taxonomy 'machine type'
Like this:

- Post Brand A - Title A
- Post Brand A - Title B
- Post Brand A - Title C

- Post Brand B - Title A
- Post Brand B - Title B
...

Seems that it's not possible to order a view by taxonomy, so how can I develop this?

I could do a taxonomy view (machine brands) sorted by its terms names, with a nested view of Machine posts ordered by their title, but then I couldn't have the filter as I've described above, right ...? (please let me know otherwhise)

If the solution is to use the action 'wpv_filter_query_post_process', can you give me an example, as my tests with that action are not affecting the views' results?

thanks.

#617360

Nigel
Supporter

Languages: English (English ) Spanish (Español )

Timezone: Europe/London (GMT+00:00)

Hi Nelson

It is a limitation of WordPress that ordering posts by taxonomies is not available, and hence it is not available in Views, which uses the standard WordPress classes for querying posts.

(If you are interested you can read some of the background to this here: hidden link)

You are correct that, if you replaced your current View with a taxonomy View that ordered the terms and nested a post View, you would lose the ability to use the filters as needed.

So, yes, you would need to manually sort the query results with custom code using the wpv_filter_query_post_process filter. (https://toolset.com/documentation/programmer-reference/views-filters/#wpv_filter_query_post_process)

If you add the following, editing the ID of the View in question, it will output the list of posts returned by the View to your debug.log file so that you can see what it is you need to manipulate (i.e. the array of post objects).

#617367

Hello Nigel,

Thanks for your answer.
I can confirm from Otto's post that this is a recurring question and why ...

Is there some code missing at the end of your message? You mention "If you add the following, editing the ID of the View in question", but I see nothing bellow.

#617370

Nigel
Supporter

Languages: English (English ) Spanish (Español )

Timezone: Europe/London (GMT+00:00)

Sorry, I omitted the code. Let me try again.

add_filter( 'wpv_filter_query_post_process', 'tssupp_reorder_posts', 10, 3 );
function tssupp_reorder_posts( $query, $view_settings, $view_id ){
	
	if ( 101 == $view_id ){

		error_log(print_r($query->posts, true));
	}

	return $query;
}
#617381

I've replace your code with this one:

add_filter( 'wpv_filter_query_post_process', 'tssupp_reorder_posts', 10, 3 );
function tssupp_reorder_posts( $query, $view_settings, $view_id ){
     
    if ( 101 == $view_id ){
       $query->query_vars['posts_per_page'] = 1;
    }
 
    return $query;
}

Should I expect my multiple posts list to be resumed to 1 post?
I've tried this and other changes on the query_vars array and nothing happened.
Is the purpose of this filter to manipulate the query_vars array OR the $query->posts ?

#617383

NOTE: just to add that I've changed the views ID to my own, of course, and confirmed that It outputs to the log file.

#617385

Nigel
Supporter

Languages: English (English ) Spanish (Español )

Timezone: Europe/London (GMT+00:00)

This filter runs after Views has used WP_Query to query the database, you cannot change any query settings to modify the results of the query, the intention is to take the results of the query and manipulate them directly as you require.

If your case you want to change the order of the returned posts.

$query includes an object, $query->posts, which is an array of post objects of the posts returned by the query.

This is the list of posts that you should manipulate to change the order.

#617416

Thanks, Nigel.
That's true, I only need to reorder.

I've managed to do that like this:

add_filter( 'wpv_filter_query_post_process', 'tssupp_reorder_posts', 10, 3 );
function tssupp_reorder_posts( $query, $view_settings, $view_id ){

    if ( 126 == $view_id ) {

        //error_log(print_r($query->posts, true));

		// Nest posts inside taxonomy brand terms
		$posts_by_brand = array();
		foreach ( $query->posts as $post ) {
			$terms = wp_get_post_terms( $post->ID, 'machine-brands', array('orderby' => 'name', 'order' => 'DESC', 'fields' => 'names') );
			if ( ! empty( $terms )  && empty( $posts_by_brand[ $terms[0] ]  ) ) {
				$posts_by_brand[ $terms[0] ]  = array();
			}
			 array_push( $posts_by_brand[ $terms[0] ], $post );
		}

		// Sort brands alphabetically
		ksort( $posts_by_brand );

		// Update $query->posts array
		$query->posts = array();
		foreach ( $posts_by_brand as $brand => $post ) {
			if ( ! empty( $posts_by_brand[ $brand ]  ) ) {
				foreach ( $posts_by_brand[ $brand ] as $post  )  {
					$query->posts[] = $post;;
				}
			}
		}

    }
 
    return $query;
}

It's working, but there's one detail missing: a separator for each brand. To group posts visually bellow each Brand.
How do you think I could achieve this?
Is there a way to maintain a loop variable on the view, so as to know the current post's brand and print an HTML separator only before each brand's first appearance?

#617866

Nigel
Supporter

Languages: English (English ) Spanish (Español )

Timezone: Europe/London (GMT+00:00)

Hi Nelson

I've done something similar by creating a custom shortcode that you can include within the wpv-loop tags of the Loop Output editor.

This custom shortcode declares a global variable which, in this case, you might use to store the current post's brand.

Then when the loop iterates to the next item, the shortcode will run again and you can compare the current post's brand to the one stored in the global variable to see if it is the same or if it has changed, in which case you would echo a separator.

https://developer.wordpress.org/plugins/shortcodes/basic-shortcodes/

From the above it looks like you are comfortable writing the PHP, but if you need more help let me know.

#618005

Hello Nigel,

it's working! Inspired in your suggestion, I've decided to create a custom function to make a conditional evaluation.
That way, I can change the HTML ouput on the views editor, which can be convenient for working with designers making adjustments in views.

Here's the code:

function anets_post_has_term_separator( $term_name, $type, $object )
{
	global $stored_loop_term;
 
        if ( ! empty( $term_name ) && (  empty( $stored_loop_term ) || $term_name != $stored_loop_term ) ) {
		$stored_loop_term = $term_name;
		return true;
	}

	return false;
}

I've added this function to the list of allowed conditional functions in Toolset settings.
Then, in the loop output editor for the view:

[wpv-conditional if="( anets_post_has_term_separator( [wpv-post-taxonomy type='machine-brands' format='name']) eq true )"]
    <div class="row brand-separator">
           <div class="col col-sm-12">
              	   <strong>[wpv-post-taxonomy type="machine-brands" format="name"]</strong>
            </div>
    </div>
[/wpv-conditional]

I know this only works because the loop was previously ordered by the Taxonomy 'machine brands' and a post can only be associated with one machine-brand at a time (I'm using the plugin 'Radio Buttons for Taxonomies' for that).
For posts with multiple terms selected, grouping would have to repeat the posts for each term associated ...

Of course, if some similar functionality existed within Views, it would be perfect.

Thanks for all your help!

#1207055

I am trying to do this exact same thing, and everything is working except for the very last step. I can't get the wpv-conditional shortcodes to work to add the term separators. I have the function in the functions.php file for my theme, exactly as it is above, and this is my loop in my loop editor.

<wpv-loop>
      [wpv-conditional if="( anets_post_has_term_separator( [wpv-post-taxonomy type='school-of' format='name']) eq true )"]
        <strong>[wpv-post-taxonomy type="school-of" format="name"]</strong>
		[wpv-post-body view_template="loop-item-in-no-layout"]
 	[/wpv-conditional]
	</wpv-loop>

My taxonomy is called "school-of". Where did I go wrong? It displays fine without the function and conditional tags, but it shows the taxonomy name before every post, and I only want it before the first post of each kind.

Thanks!