Displaying Content
on Your Terms
with WP_Query
What are we going to learn today?
- About Jim
- About WP_Query
- What is it like to have god-like powers over WordPress content?
- Responsible use of said god-like powers.
- Questions
About Jim
- Web developer for STAT
- Co-Organize:
- BostonWP
- WordCamp Boston
- New England GiveCamp
- In the technolgy realm for over 20 years.
- Web developer for 7 years
WP_Query is what makes the WordPress go around.
Basic structure.
// WP_Query arguments
$args = array(
'post_type' => array( 'books' ), //Can be 'posts', 'pages'
'post_status' => array( 'publish', ' private' ), // 'draft', 'scheduled' are also options
'nopaging' => false,
'paged' => '5',
'posts_per_page' => '50',
'order' => 'DESC',
'orderby' => 'title',
);
// The Query
$query = new WP_Query( $args );
// The Loop
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
// do something
}
} else {
// no posts found
}
// Restore original Post Data
wp_reset_postdata();
Page "archive" of STAT Plus posts
function stat_the_paywall_loop() {
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
$paywall_posts = method_exists( '\STAT\Paywall\Admin', 'get_paywall_posts' ) ? \STAT\Paywall\Admin::get_paywall_posts( null, $paged ) : '';
if ( empty( $paywall_posts ) || ! class_exists( '\STAT\Paywall\Archive' ) ) {
return;
}
if ( is_page_template( \STAT\Paywall\Archive::get_instance()->template_path_user ) && $paywall_posts ) {
global $wp_query;
$paywall_posts->is_home = false; //We don't want homepage-based ads
$paywall_posts->is_category = true; // We DO want category-based ads
$paywall_posts->is_author = true; // Because we are setting it as a category, we lose the kickers. Tricking the query to treat it as an author page brings them back.
$orig = $wp_query;
// @codingStandardsIgnoreStart
$wp_query = $paywall_posts;
get_template_part( 'template-parts/content', 'loop' );
$wp_query = $orig;
// @codingStandardsIgnoreEnd
}
wp_reset_postdata();
}
function get_paywall_posts( $limit = null, $paged = null ) {
$_posts = null;
//Default WP_Query args
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'orderby' => 'post_date',
'order' => 'DESC',
'meta_query' => array(
array(
'key' => 'stat_paywall_restrict',
'value' => true,
),
),
);
//If a limit has been passed in, use that to restrict the posts returned.
//Otherwise, use the default limit.
if ( null !== $limit ) {
$args['posts_per_page'] = $limit;
}
// Check for the paged
if ( null !== $paged ) {
$args['paged'] = $paged;
}
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
$_posts = $query;
}
wp_reset_postdata();
return $_posts;
}
Prepend Zoninator posts before the main Loop
function update_home_query( $query ) {
if ( ! $query->is_main_query() || ! $query->is_home() ) {
return;
}
if ( ! function_exists( 'z_get_posts_in_zone' ) ) {
return;
}
//Get the war story if it exists.
$war_id = self::get_war_zone_post_id();
if ( $war_id ) {
$war_id = array( $war_id );
} else {
$war_id = array();
}
//Grab the existing post__not_in
$current_not_in = $query->get( 'post__not_in' );
/**
* We want the ability for the editors to populate the home page with the majority of
* posts from Zoninator. However, since WP needs something to populate the home page, and
* doesn't really like posts_per_page to be 0, I'm giving it the bottom row. This will
* allow the editors to have all but the bottom three posts to work with.
*/
$posts_per_page = get_option( 'posts_per_page' );
$this->zone_posts_amt = $posts_per_page - 3;
self::$home_zone_posts = z_get_posts_in_zone( self::$home_zone_slug, array( 'posts_per_page' => $this->zone_posts_amt, 'post__not_in' => $war_id ) );
$home_zone_post_ids = wp_list_pluck( self::$home_zone_posts, 'ID' );
$this->zone_count = count( self::$home_zone_posts );
if ( ! $query->is_paged ) {
$query->set( 'posts_per_page', ( $posts_per_page - $this->zone_count ) );
} else {
$offset = ( ( $query->query_vars['paged'] - 1 ) * $posts_per_page ) - $this->zone_count;
$query->set( 'posts_per_page', $posts_per_page );
$query->set( 'offset', $offset );
}
$home_n_war_zones = array_merge( $home_zone_post_ids, $war_id );
$post_not_in = array_merge( $current_not_in, $home_n_war_zones );
$query->set( 'post__not_in', $post_not_in );
}
Using the $query global to filter taxonomies.
function stat_exclude_taxonomy( $query, $taxonomy, $terms = array() ) {
$field = 'slug';
$tax_query = $query->get( 'tax_query' );
if ( ! $tax_query ) {
$tax_query = array();
}
if ( ! count( $terms ) ) {
// $terms is empty, we're making the assumption that caller wants to exclude
// all terms of the given $taxonomy for this $query.
$term_ids = get_terms( array(
'taxonomy' => $taxonomy,
'fields' => 'ids',
) );
if ( is_array( $term_ids ) ) {
$field = 'term_id';
$terms = $term_ids;
}
}
$tax_query[] = array(
'taxonomy' => $taxonomy,
'field' => $field,
'terms' => $terms,
'operator' => 'NOT IN',
);
$query->set( 'tax_query', $tax_query );
}
Using the meta_query paramater
function zone_schedule( $action ) {
if ( ! in_array( $action, array( 'start', 'end' ), true ) || ! function_exists( 'z_get_zoninator' ) ) {
return;
}
switch ( $action ) {
case 'start':
$key = self::$metabox_name . '_schedule_start_date';
break;
case 'end':
$key = self::$metabox_name . '_schedule_end_date';
break;
default:
break;
}
$now = time();
$args = array(
'post_type' => StatSponsoredContent::$post_type,
'post_status' => 'publish',
'posts_per_page' => 20, // Process max 20 Sponsor Pages per call.
'meta_query' => array(
'relation' => 'AND',
array(
'key' => self::$metabox_name . '_schedule_enable',
'value' => 1,
),
array(
'key' => $key,
'compare' => '<=',
'value' => $now,
'type' => 'NUMERIC',
),
),
);
if ( 'start' === $action ) {
// Add additional meta query clause for 'start' action to prevent us
// from attempting to process Sponsor Pages that have already passed
// their scheduled end date.
$args['meta_query'][] = array(
'key' => self::$metabox_name . '_schedule_end_date',
'compare' => '>=',
'value' => $now,
'type' => 'NUMERIC',
);
}
$query = new WP_Query( $args );
while ( $query->have_posts() ) {
$query->the_post();
$_post = get_post();
$zone_id = (int) $_post->{self::$metabox_name . '_zone_id'};
if ( ! $zone_id ) {
// Scheduling was enabled but no Zone was selected, skip this Post.
continue;
}
switch ( $action ) {
case 'start':
$this->add_sponsor_to_zone( $zone_id, $_post );
break;
case 'end':
z_get_zoninator()->remove_zone_posts( $zone_id, $_post );
// Clear the current Post's schedule enable flag so that we don't
// attempt to remove it from the Zone again during the next cron run.
delete_post_meta( $_post->ID, self::$metabox_name . '_schedule_enable' );
break;
default:
break;
}
}
wp_reset_postdata();
}
Using the $wpdb global
public function action_comments_custom_column( $column, $comment_id ) {
global $wpdb;
$cats = array();
if ( 'category' === $column ) {
// @codingStandardsIgnoreStart
$term_ids = $wpdb->get_col( $wpdb->prepare(
"SELECT term.term_taxonomy_id FROM {$wpdb->term_relationships} as term "
. "JOIN {$wpdb->term_taxonomy} as tax "
. "ON term.term_taxonomy_id = tax.term_id "
. "JOIN {$wpdb->posts} as post "
. "ON term.object_id = post.ID "
. "JOIN {$wpdb->comments} as comments "
. "ON post.ID = comments.comment_post_ID "
. "WHERE tax.taxonomy = 'category' "
. "AND comments.comment_ID = '%s'",
$comment_id
) );
// @codingStandardsIgnoreEnd
if ( ! empty( $term_ids ) ) {
foreach ( $term_ids as $id ) {
$cat = get_category( $id );
$cats[] = $cat->name;
}
$cat_implosion = implode( ', ', $cats );
echo esc_html( $cat_implosion );
}
}
}
Interacting with the posts_search filter
public function filter_coauthors_search( $posts_search, $query ) {
global $wpdb, $coauthors_plus;
$search = $this->get_search( $query );
// Don't modify the query at all if we're not on the search template
// or if the LIKE is empty
if ( empty( $posts_search ) || ! $search ) {
return $posts_search;
}
// Find the author "term_ids" based on the search query
$co_terms = get_terms( $coauthors_plus->coauthor_taxonomy, array(
'orderby' => 'name',
'hide_empty' => false,
'description__like' => $search,
) );
// Don't modify the query if there aren't any matching authors
if ( empty( $co_terms ) ) {
return $posts_search;
}
$co_ids = wp_list_pluck( $co_terms, 'term_taxonomy_id' );
// Add the tax_ids to the search string.
$posts_search = str_replace( ')))', ")) OR ( {$wpdb->term_relationships}.term_taxonomy_id IN(" . implode( ',', $co_ids ) . ')))', $posts_search );
return $posts_search;
}
/**
* Add a join on term_relationships to allow
* searching on coauthors term
* @global object $wpdb
* @param string $join
* @return string
*/
public function filter_coauthors_join( $join, $query ) {
global $wpdb;
$search = $this->get_search( $query );
if ( ! $search ) {
return $join;
}
// Since a category search is already joining the term_relationships,
// we don't want this one to happen.
if ( $query->is_category() ) {
return $join;
}
$join .= " LEFT JOIN {$wpdb->term_relationships} ON {$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id";
return $join;
}