You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 21:37:53