
Using WP Meta Query to Mix Custom Post Types with Regular Posts
- August 11, 2016
- Leave a comment
WordPress provides a variety of content with custom post types and custom meta fields. The WP_Query class allows you to fetch almost anything from your WP content. meta_query support has been part of WP_Query since WordPress 3.1 release, in order to fetch content based on certain meta fields. With the help of custom post types and meta fields, you can add and retrieve any type of content. In this article, you will learn the mixing of regular post type with custom post type on a homepage using meta-query.
Let’s say you have a custom post type named as “Books” with custom meta fields “Author” and “Genre” and you only want the Books from a specific “Genre” to be displayed on your homepage as main posts query.
If you add meta query directly to the main query, it will cause an issue with regular posts. Reason being, it will try to match the posts with certain meta fields, which the regular posts does not contain. So, it will not retrieve the regular posts from the main query. Here, you need to add meta query on only one of the post types which is generally not possible with WP_Query.
To achieve this functionality, you need to add filters to add join and where conditions, respectively, for adding meta query on only one post type and leave others intact. Now add pre_get_post action and then add post_join and post_where filters as follows:
1 2 3 4 5 6 7 8 9 10 11 12 |
/** * Run on pre_get_posts and if on home page * add posts_where, posts_join and pre_get_posts hooks */ function home_page_books_sql( $query ) { // exit if is not main query and home index if(is_home() && $query->is_main_query() && !is_admin()){ add_filter('posts_where', 'home_page_custom_filter'); add_filter('posts_join', 'home_page_custom_filter'); } } add_action('pre_get_posts', 'home_page_custom_sql'); |
The above action will determine if the query, which is currently active, is the main homepage query. After that, it will add filters for custom post type on the homepage query only.
Now, add the following function to retrieve the content from both regular and custom posts based on meta query. It will only affect the custom post type.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
/** * Set the SQL filtering posts_join and posts_where * use WP_Meta_Query to generate the additional where clause */ function home_page_books_filter( $sql = '' ) { // remove filters remove_filter( current_filter(), __FUNCTION__); static $sql_custom_filters; if ( is_null($sql_custom_filters) ) { // SET YOUR META QUERY ARGS HERE $args = array( array( 'key' => 'my_custom_key', 'value' => 'value_your_are_looking_for', 'compare' => '=' ) ); $meta_query = new WP_Meta_Query( $args ); $sql_custom_filters = $meta_query->get_sql('post', $GLOBALS['wpdb']->posts, 'ID'); } // SET YOUR CPT NAME HERE $cpt = 'your_custom_post_type_name'; global $wpdb; if ( current_filter() === 'posts_where' && isset($sql_custom_filters['where']) ) { $where = "AND ($wpdb->posts.post_status = 'publish') "; $where .= "AND ( $wpdb->posts.post_type = 'post' OR ( "; $where .= $wpdb->prepare( "$wpdb->posts.post_type = %s", $cpt); $where .= $sql_custom_filters['where'] . ' ) )'; $where .= " GROUP BY $wpdb->posts.ID "; return $where; } if ( current_filter() === 'posts_join' && isset($sql_custom_filters['join']) ) { return $sql .= $sql_custom_filters['join']; } } |
The above function will remove all other filters (that are being used for either posts_join or posts_where) that are applying to the query, in order to add the where clause, that has to be implemented with the custom meta query. After that, you have to add your custom filters for the meta query, using meta query array with WP_Meta_Query class to get the exact SQL (that was used with the where clause for custom post type).
The where clause has an OR condition between regular post type and custom post type, whereas, the custom post type query has an AND condition between post published state and post meta value. This will enable the query to get all posts from regular post type and the specific posts from custom post type that contain a certain meta value for the current page which is called.
Lastly, the result will be grouped by the post IDs and returned to the WP Query object, which will get the post data that is to be shown within the loop. This can be done for any number of post types, and within any of the WordPress queries, wherever needed.
Thanks for this! This seems to be exactly the answer to the issue I’m having with complex queries. However, I sort of need this reversed and am not entirely sure how to do it.
I have a custom post type called ‘support_articles’ that should show all of its own posts AND I want to add in regular old posts with either the categories or tags with term ‘tips’ or ‘tip’.
So basically I’m wanting to Join some regular posts with certain taxonomy terms to the CPT archive query, and not using the Meta Query.
How would one reverse what you’ve done here to that effect? This seems to me to be an ongoing need in order to mix content.