WordPress自定义文章类型关联查询:排除Volunteers分类的Show
问题描述
我有5个自定义文章类型(CPT):
- Persons:包含标题、名、姓字段
- Show:包含演出年份、开幕/闭幕日期,关联
category分类法 - Position:包含职位名称、排序数字字段
- Cast:包含角色、关联人员、关联Show字段
- Crew:包含职位、关联人员、关联Show字段
需要在Persons单页中展示该用户作为Crew参与的所有Show,但要排除分类为Volunteers的Show。目前的WP_Query已经能关联Crew和Persons,但添加tax_query排除指定分类未生效,求正确实现方案。
当前无效的尝试代码
最初的查询直接在Crew的WP_Query中添加了tax_query,但因为分类属于Show而非Crew,所以无法生效:
// 获取当前Person的ID $post_id = get_the_ID(); // Crew查询 $crew_query = new WP_Query(array( 'post_type' => 'crew', 'posts_per_page' => -1, 'post_status' => 'publish', 'meta_query' => array( array( 'key' => 'techperson', 'value' => $post_id, 'compare' => '=', ), ), // 这里的tax_query作用于Crew,而非关联的Show,所以无效 'tax_query' => array( array( 'taxonomy' => 'category', 'field' => 'slug', 'terms' => 'volunteers', 'operator' => 'NOT IN', ), ), 'orderby' => 'meta_value_num', 'meta_key' => 'show', 'order' => 'ASC', )); // 后续输出代码省略
正确实现方案
方案一:先筛选符合条件的Show,再查询关联的Crew
先获取所有不属于Volunteers分类的Show的ID,再查询关联当前Person且关联这些Show的Crew:
// 获取当前Person的ID $post_id = get_the_ID(); // 第一步:获取排除Volunteers分类的所有Show的ID $valid_show_ids = get_posts(array( 'post_type' => 'show', 'posts_per_page' => -1, 'post_status' => 'publish', 'tax_query' => array( array( 'taxonomy' => 'category', 'field' => 'slug', 'terms' => 'volunteers', 'operator' => 'NOT IN', ), ), 'fields' => 'ids', // 只返回ID,提升性能 )); // 第二步:查询关联当前Person且关联上述Show的Crew $crew_query = new WP_Query(array( 'post_type' => 'crew', 'posts_per_page' => -1, 'post_status' => 'publish', 'meta_query' => array( 'relation' => 'AND', array( 'key' => 'techperson', 'value' => $post_id, 'compare' => '=', ), array( 'key' => 'show', // ACF关联字段存储的是Show的ID 'value' => $valid_show_ids, 'compare' => 'IN', ), ), 'orderby' => 'meta_value_num', 'meta_key' => 'show', 'order' => 'ASC', )); // 输出部分代码 if ($crew_query->have_posts()) : ?> <h3>作为工作人员参与的演出:</h3> <ul class="castcrewlist"> <?php while ($crew_query->have_posts()) : $crew_query->the_post(); ?> <?php $crewshowID = get_field('show', false, false); $position = get_field('crewposition'); $positionname = $position ? get_the_title($position) : ''; $year = get_field('year', $crewshowID); ?> <li> <em> <a href="<?php echo get_permalink($crewshowID); ?>"><?php echo get_the_title($crewshowID); ?></a> <?php if ($year) : ?> <a href="<?php echo home_url('/past-seasons/?season=') . $year; ?>"> (<?php echo $year; ?>) </a> <?php endif; ?> </em> <?php if ($positionname) : ?> <strong><?php echo $positionname; ?></strong> <?php endif; ?> </li> <?php endwhile; ?> </ul> <?php endif; wp_reset_postdata(); // 注意用wp_reset_postdata()替代wp_reset_query(),更规范
方案二:使用SQL JOIN直接关联Crew和Show的分类(性能更优)
通过posts_join和posts_where钩子,直接在数据库层面关联Crew和Show的分类信息,避免两次查询:
// 获取当前Person的ID $post_id = get_the_ID(); // 定义钩子函数,添加JOIN和WHERE条件 add_filter('posts_join', function($join) { global $wpdb; // 关联Crew的meta表(存储show的ID)和Show的分类关系表 $join .= " INNER JOIN {$wpdb->postmeta} cm ON {$wpdb->posts}.ID = cm.post_id AND cm.meta_key = 'show' LEFT JOIN {$wpdb->term_relationships} tr ON cm.meta_value = tr.object_id LEFT JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id LEFT JOIN {$wpdb->terms} t ON tt.term_id = t.term_id "; return $join; }); add_filter('posts_where', function($where) { global $wpdb; // 排除Volunteers分类的Show $where .= " AND (t.slug != 'volunteers' OR t.slug IS NULL) "; return $where; }); // 执行Crew查询 $crew_query = new WP_Query(array( 'post_type' => 'crew', 'posts_per_page' => -1, 'post_status' => 'publish', 'meta_query' => array( array( 'key' => 'techperson', 'value' => $post_id, 'compare' => '=', ), ), 'orderby' => 'meta_value_num', 'meta_key' => 'show', 'order' => 'ASC', )); // 移除钩子,避免影响后续查询 remove_filter('posts_join', '__return_false'); remove_filter('posts_where', '__return_false'); // 输出代码同方案一 if ($crew_query->have_posts()) : ?> <h3>作为工作人员参与的演出:</h3> <ul class="castcrewlist"> <?php while ($crew_query->have_posts()) : $crew_query->the_post(); ?> <?php $crewshowID = get_field('show', false, false); $position = get_field('crewposition'); $positionname = $position ? get_the_title($position) : ''; $year = get_field('year', $crewshowID); ?> <li> <em> <a href="<?php echo get_permalink($crewshowID); ?>"><?php echo get_the_title($crewshowID); ?></a> <?php if ($year) : ?> <a href="<?php echo home_url('/past-seasons/?season=') . $year; ?>"> (<?php echo $year; ?>) </a> <?php endif; ?> </em> <?php if ($positionname) : ?> <strong><?php echo $positionname; ?></strong> <?php endif; ?> </li> <?php endwhile; ?> </ul> <?php endif; wp_reset_postdata();
关键说明
- 原代码的问题在于
tax_query直接作用于Crew自定义文章类型,但Volunteers分类是属于Show的,所以无法过滤目标内容。 - 方案一逻辑简单易维护,适合数据量不大的场景;方案二通过一次数据库查询完成,性能更优,适合数据量较大的站点。
- 注意使用
wp_reset_postdata()替代wp_reset_query(),因为wp_reset_query()是针对query_posts()的,而WP_Query应该用wp_reset_postdata()来恢复全局post数据。
内容的提问来源于stack exchange,提问作者omarkoza
相关产品推荐
相关产品推荐

