如何通过分类术语从自定义 WordPress MySQL 查询中排除结果

Posted

技术标签:

【中文标题】如何通过分类术语从自定义 WordPress MySQL 查询中排除结果【英文标题】:How To Exclude Results From Custom WordPress MySQL Query By Taxonomy Term 【发布时间】:2015-09-11 07:34:37 【问题描述】:

我只想显示分类“product-brand”中没有术语“brand-slug”的帖子。

我当前的查询没有应用过滤器:

SELECT DISTINCT * FROM $wpdb->posts AS p
  LEFT JOIN $wpdb->postmeta AS meta ON p.ID = meta.post_id
  LEFT JOIN $wpdb->term_relationships AS rel ON rel.object_id = p.ID
  LEFT JOIN $wpdb->term_taxonomy AS tax ON tax.term_taxonomy_id = rel.term_taxonomy_id
  LEFT JOIN $wpdb->terms AS term ON tax.term_id = term.term_id
WHERE 1=1
    AND p.post_type = 'product' 
    AND p.post_status = 'publish' 
    AND p.post_title LIKE '%$trimmed%' OR (meta.meta_key = 'product_model' AND meta.meta_value LIKE '%$trimmed%') 
    AND (tax.taxonomy = 'product-brand' AND term.slug NOT IN ('$protected'))

分类法或 slug 条件似乎在上述查询中都不起作用。

感谢任何帮助!

【问题讨论】:

【参考方案1】:

注意事项:

您似乎没有使用$wpdb->prepare(),因此您面临着 SQL 注入的风险。

我还认为您在相关 OR 部分周围缺少括号,因此您最终不会显示草稿。

替代方案:

我们应该能够使用WP_Query 类,而不是编写硬编码的 SQL 查询,并通过钩子/过滤器进行一些修改。

这是一个示例(php 5.4+):

$args = [ 
    '_meta_or_like_title' => $trimmed,        // Our new custom argument!
    'post_type'           => 'product',
    'post_status'         => 'publish',
    'meta_query'          => [
        [
            'key'     => 'product_model',
            'value'   => $trimmed,            // Your meta value
            'compare' => 'LIKE'
        ]
    ],
    'tax_query'    => [
        [
            'taxonomy'  => 'product-brand',
            'field'     => 'slug',
            'terms'     => $protected,        // Your terms array
            'operator'  => 'NOT IN'
        ]
    ]
];

自定义_meta_or_like_title 参数由我为另一个question here 编写的稍微修改的插件支持。

插件:

<?php
/**
 *  Plugin Name:   Meta OR LIKE Title query in WP_Query
 *  Description:   Activated through the '_meta_or_like_title' argument of WP_Query 
 *  Plugin URI:    http://***.com/a/31241416/2078474
 *  Plugin Author: Birgir Erlendsson (birgire)
 *  Version:       0.0.1
 */

add_action( 'pre_get_posts', function( $q )

    if( $title = $q->get( '_meta_or_like_title' ) )
    
        add_filter( 'get_meta_sql', function( $sql ) use ( $title )
        
            global $wpdb;

            // Only run once:
            static $nr = 0; 
            if( 0 != $nr++ ) return $sql;

            // Modify WHERE part:
            $sql['where'] = sprintf(
                " AND ( %s OR %s ) ",
                $wpdb->prepare( 
                    "$wpdb->posts.post_title LIKE '%%%s%%'", 
                     $wpdb->esc_like( $title ) 
                ),
                mb_substr( $sql['where'], 5, mb_strlen( $sql['where'] ) )
            );
            return $sql;
        );
    
, PHP_INT_MAX );

【讨论】:

我写了完全相同的方法(我中途放弃了),因为我遇到了按标题或自定义字段搜索帖子的过滤。我实际上采用了posts_* 过滤方式。喜欢你的自定义方法比我的更好:-) 我们需要以某种方式掌握整个元查询,因此posts_* 过滤器不如get_meta_sql 过滤器合适。但也许posts_* 过滤器有一种方法,通过添加一个优先级为0 和另一个优先级为PHP_INT_MAX 的过滤器?但是我们仍然需要识别元查询,所以我想用这种方法我们需要更多的东西;-) @PieterGoosen 可能是的,但一段时间后它会变得非常混乱。我尝试结合一些没有成功的方法。正如我所说,您的方法肯定更好,而且麻烦更少。这里的可重用性很好。您有一个简单的功能,一个额外的参数,您可以在数百个自定义查询中重复使用,而无需复制和修改您的自定义过滤器 是的,这听起来像是一个更困难的挑战;-) 我曾经扩展 WP_Query 类以获得额外的东西。但现在我更喜欢通过自定义参数添加新功能,如果插件被删除并且使用前缀wpse_或只是_,在自定义参数名称中,我们应该能够避免使用名称碰撞。但是这种方法的好处是我们也可以通过pre_get_posts 在主要查询中使用它;-) @PieterGoosen 我必须说,你让我迷上了这种方法 ;-)

以上是关于如何通过分类术语从自定义 WordPress MySQL 查询中排除结果的主要内容,如果未能解决你的问题,请参考以下文章

Wordpress - 从自定义帖子类型中删除子菜单

Wordpress ACF 字段如何从自定义帖子类型中获取选项

如何从自定义页面模板调用 WordPress 插件功能?

Wordpress分类术语还有内容的仪表板页面?

在 WordPress 上浏览自定义分类术语

Wordpress 获取不在自定义分类术语中的帖子