如何在Wordpress中同时查询分类法或自定义字段?

时间:2014-03-24 14:09:15

标签: wordpress

我需要查询wordpress帖子,返回可以通过类别OR自定义字段匹配的结果。如果它们与类别AND自定义字段匹配,则使用以下代码仅返回结果。我需要找到可以通过任一查询匹配的匹配项,因此“OR”结果但似乎不起作用。

有人可以帮忙吗?

$args =  array(
'post_type' => 'post',
'tax_query' => array(
'relation' => 'OR',
    array(
        'taxonomy'  => 'category',
        'field'     => 'slug',
        'terms' => array( 'arts','food' ),
        'operator'  => 'IN'
    )
),
'meta_query' => array(
   'relation' => 'OR',
    array(
        'key' => 'birth_city',
        'value' => 'London',
        'compare' => 'IN',
      ),
   )
);

3 个答案:

答案 0 :(得分:4)

版本0.2

这是我之前回答的简化 - 我希望你不要介意我把它作为一个新答案添加,以避免与我之前的答案混淆。

在这里,我使用WordPress函数get_meta_sql()get_tax_sql()来修改由WHERE生成的SQL的WP_Query()部分:

 /**
 * Class WPSE_OR_Query 
 * 
 * Add a support for (tax_query OR meta_query) 
 *
 * @version 0.2
 * @link http://stackoverflow.com/a/22633399/2078474
 *
 */
class WPSE_OR_Query extends WP_Query 
{       
    protected $meta_or_tax  = FALSE;
    protected $tax_args     = NULL;
    protected $meta_args    = NULL;

    public function __construct( $args = array() )
    {
        add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ), 999 );
        add_filter( 'posts_where',   array( $this, 'posts_where' ),   999 );
        parent::__construct( $args );
    }
    public function pre_get_posts( $qry )
    {       
        remove_action( current_filter(), array( $this, __FUNCTION__ ) );            
        // Get query vars
        $this->meta_or_tax = ( isset( $qry->query_vars['meta_or_tax'] ) ) ? $qry->query_vars['meta_or_tax'] : FALSE;
        if( $this->meta_or_tax )
        { 
            $this->tax_args  = ( isset( $qry->query_vars['tax_query'] ) )  ? $qry->query_vars['tax_query']  : NULL;
            $this->meta_args = ( isset( $qry->query_vars['meta_query'] ) ) ? $qry->query_vars['meta_query'] : NULL;
        }
    }
    public function posts_where( $where )
    {       
        global $wpdb;       
        $field = 'ID';
        remove_filter( current_filter(), array( $this, __FUNCTION__ ) );    

        // Construct the "tax OR meta" query
        if( $this->meta_or_tax && is_array( $this->tax_args ) &&  is_array( $this->meta_args )  )
        {
            // Tax query
            $sql_tax = get_tax_sql( $this->tax_args, $wpdb->posts, $field );            

            // Meta query
            $sql_meta = get_meta_sql( $this->meta_args, 'post', $wpdb->posts, $field );

            // Modify the 'where' part          
            if( isset( $sql_meta['where'] ) && isset( $sql_tax['where'] ) )
            {
                $where  = str_replace( array( $sql_meta['where'], $sql_tax['where'] ) , '', $where );
                $where .= sprintf( ' AND ( %s OR  %s ) ', substr( trim( $sql_meta['where'] ), 4 ), substr( trim( $sql_tax['where'] ), 4 ) );              
            }
        }   
        return $where;
    }
}

答案 1 :(得分:3)

这是解决问题的第一次尝试。我在以下限制范围内工作:

  • 不直接编写SQL查询
  • 扩展WP_Query
  • 使用WP_Tax_QueryWP_Meta_Query
  • 不要在SQL查询中使用preg_replace / str_replace

这可能太受限制,但这是一个挑战; - )

所以你可以试试:

$args =  array(
    'post_type' => 'post',
    'meta_or_tax' => TRUE,
    'tax_query' => array(
            array(
                'taxonomy'  => 'category',
                'field'     => 'slug',
                'terms' => array( 'birds','falcons' ),
                'operator'  => 'IN'
            )
    ),
    'meta_query' => array(
            array(
                'key' => 'birth_city',
                'value' => 'London',
                'compare' => 'IN',
            ),
    )
);

$q = new WPSE_OR_Query( $args );

我们引入新参数meta_or_tax

`meta_or_tax` => FALSE 

会为您提供正常的WP_Query行为和

`meta_or_tax` => TRUE 

将允许您在纳税查询元查询之间获得OR而非默认AND

新的WPSE_OR_Query类定义为:

 /**
 * Class WPSE_OR_Query
 *
 * Add a support for (tax_query OR meta_query) 
 *
 * @link http://stackoverflow.com/a/22616337/2078474
 *
 */
class WPSE_OR_Query extends WP_Query 
{       
    protected $meta_or_tax  = FALSE;
    protected $tax_args     = NULL;
    protected $meta_args    = NULL;

    public function __construct( $args = array() )
    {
        add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ), 10 );
        add_filter( 'posts_clauses', array( $this, 'posts_clauses' ), 10 );
        parent::__construct( $args );
    }

    public function pre_get_posts( $qry )
    {       
        remove_action( current_filter(), array( $this, __FUNCTION__ ) );            
        // Get query vars
        $this->meta_or_tax = ( isset( $qry->query_vars['meta_or_tax'] ) ) ? $qry->query_vars['meta_or_tax'] : FALSE;
        if( $this->meta_or_tax )
        { 
            $this->tax_args = ( isset( $qry->query_vars['tax_query'] ) ) ? $qry->query_vars['tax_query'] : NULL;
            $this->meta_args = ( isset( $qry->query_vars['meta_query'] ) ) ? $qry->query_vars['meta_query'] : NULL;
            // Unset meta and tax query
            unset( $qry->query_vars['meta_query'] );
            unset( $qry->query_vars['tax_query'] );
        }
    }

    public function posts_clauses( $clauses )
    {       
        global $wpdb;       
        $field = 'ID';
        remove_filter( current_filter(), array( $this, __FUNCTION__ ) );    
        // Reconstruct the "tax OR meta" query
        if( $this->meta_or_tax && is_array( $this->tax_args ) &&  is_array( $this->meta_args )  )
        {
            // Tax query
            $tax_query = new WP_Tax_Query( $this->tax_args );
            $sql_tax = $tax_query->get_sql( $wpdb->posts, $field );
            // Meta query
            $meta_query = new WP_Meta_Query( $this->meta_args );
            $sql_meta = $meta_query->get_sql( 'post', $wpdb->posts, $field );
            // Where part
            if( isset( $sql_meta['where'] ) && isset( $sql_tax['where'] ) )
            {
                $t = substr( trim( $sql_tax['where'] ), 4 );
                $m = substr( trim( $sql_meta['where'] ), 4 );
                $clauses['where'] .= sprintf( ' AND ( %s OR  %s ) ', $t, $m );              
            }
            // Join/Groupby part
            if( isset( $sql_meta['join'] ) && isset( $sql_tax['join'] ) )
            {
                $clauses['join']    .= sprintf( ' %s %s ', $sql_meta['join'], $sql_tax['join'] );               
                $clauses['groupby'] .= sprintf( ' %s.%s ', $wpdb->posts, $field );
            }       
        }   
        return $clauses;
    }

}

这是普通SQL的一个例子:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts 
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) 
WHERE 1=1 
AND ( wp_term_relationships.term_taxonomy_id IN (15,70) )
AND wp_posts.post_type = 'post' 
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') 
AND ( (wp_postmeta.meta_key = 'birth_city' AND CAST(wp_postmeta.meta_value AS CHAR) IN ('London')) ) 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC LIMIT 0, 10

和修改版本的SQL:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts 
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) 
WHERE 1=1 
AND wp_posts.post_type = 'post' 
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') 
AND ( ( (wp_postmeta.meta_key = 'birth_city' AND CAST(wp_postmeta.meta_value AS CHAR) IN ('London')) ) OR ( wp_term_relationships.term_taxonomy_id IN (15,70) ) ) 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC LIMIT 0, 10

这当然可以进一步完善,您可以根据自己的需要进一步扩展。

希望这有帮助。

答案 2 :(得分:1)

我没有找到一种方法如何使用WP_Query,但如果它有帮助,这里是如何手动编写查询,它看起来很丑,但如果这是唯一的方式它将达到目的:

    $results = $wpdb->get_results( $wpdb->prepare( "
        SELECT * FROM $wpdb->posts
        LEFT JOIN $wpdb->postmeta ON($wpdb->posts.ID = $wpdb->postmeta.post_id)
        LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id)
        LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
        LEFT JOIN $wpdb->terms ON($wpdb->term_taxonomy.term_id = $wpdb->terms.term_id)
        WHERE $wpdb->posts.post_status = 'publish'
        AND $wpdb->posts.post_type = 'post' 
        AND $wpdb->term_taxonomy.taxonomy = 'category'                  
        AND $wpdb->terms.name IN ( %s, %s ) 
        OR ( $wpdb->postmeta.meta_key = %s
        AND $wpdb->postmeta.meta_value = %s )
        ORDER BY $wpdb->posts.post_date DESC
    ", 'arts', 'food', 'birth_city', 'London' ), OBJECT_K );