自定义WordPress查询的4种方法如下:

  • 通过改变query_posts($args)的参数修改主循环
  • 通过WP_Query Class
  • 通过filters更改query_posts()产生的SQL语句
  • 通过$wpdb自定义SQL语句

方法一:通过改变query_posts($args)的参数修改主循环

参考:Function Reference/query posts

query_posts( $args )是用来修改WordPress主循环(Main Loop),使用时应该谨记这一点,如果你所做的查询只是希望在文章底部显示相关文章或者在widget中显示热门文章等等,不要使用query_posts(),因为这会影响与之相关的全局变量,使得主循环中输出的信息不正确。如果一定要使用,记得在做完自己的查询后调用wp_reset_query()

通过给query_posts()传参数,可以对主循环做一些简单的定制化,例如

<?php
if ( is_home() ) {
query_posts( 'cat=-3' );
}
?>

方法二:通过WP_Query Class

参考文章:Class Reference/WP Query

WP_Query是一个定义在wp-includes/query.php中的Class,query_posts()就是由WP_Query创建的$wp_query object,当然你可以创建更多的实例,这样就能有效避免对query_posts()进行修改。当然,WP_Query也具备query_posts()的特性,只需创建一个新的实例,就可以像使用query_posts()那样传参数。例如:

<?php $my_query = new WP_Query('category_name=special_cat&posts_per_page=10'); ?>
<?php while ($my_query->have_posts()) : $my_query->the_post(); ?>
  <!-- Do special_cat stuff... -->
<?php endwhile; ?>

方法三:通过filters更改query_posts()产生的SQL语句

参考文章:Custom Queries

通过query_posts() 的filters更改其产生的SQL语句,与第一种方法相比,可以做更复杂的更改,例如与其它数据库表进行联合查询、更改select选择的字段等等。并且可以享用query_posts()带来的好处,例如分页。缺点是可能查询出一些没用的数据,占用更多内存,降低执行效率。

可用的filters有:

  • posts_join
  • posts_groupby
  • posts_orderby
  • posts_distinct
  • posts_fields
  • post_limits
  • posts_where_paged
  • posts_join_paged
  • posts_request

例如联合查询

add_filter('posts_join', 'geotag_search_join' );
function geotag_search_join( $join )
{
  global $geotag_table, $wpdb;
  if( is_search() ) {
    $join .= " LEFT JOIN $geotag_table ON " . 
       $wpdb->posts . ".ID = " . $geotag_table . 
       ".geotag_post_id ";
  }
  return $join;
}

例如改变select选择的字段,默认选择wp_posts.*

add_filter('posts_fields','my_posts_field');
function my_posts_field ($fields){
	if( is_home() || is_category() ){
		$fields .= ", mytable.*";
	}
	return $fields;
}

方法四:通过$wpdb自定义SQL语句

参考:Displaying Posts Using a Custom Select Query

这种方法的优势是可定制化最强最灵活,劣势是query_posts()的好处全都没了,比如分页、后台控制首页显示多少文章等等,只有使用query_posts()时分页才会有效。

$wpdb class提供了一系列查询数据库的方法,例如$wpdb->query()允许你抛开所有wordpress的sql语句定义自己的sql。这个类基于ezSQL,语法基本没什么变化,使用很方便,对SQL语句运用自如的人通过该方法可以达到最优化的查询效果。

代码示例:

<?php
 $querystr = "
    SELECT $wpdb->posts.* 
    FROM $wpdb->posts, $wpdb->postmeta
    WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id 
    AND $wpdb->postmeta.meta_key = 'tag' 
    AND $wpdb->postmeta.meta_value = 'email' 
    AND $wpdb->posts.post_status = 'publish' 
    AND $wpdb->posts.post_type = 'post'
    AND $wpdb->posts.post_date < NOW()
    ORDER BY $wpdb->posts.post_date DESC
 ";
 $pageposts = $wpdb->get_results($querystr, OBJECT);
 ?>

通过运行一个自定义的SQL语句获得查询结果,结果保存在$pageposts中。

接下来将结果输出

<?php 
if ($pageposts) {
 
	global $post; 
	
	foreach ($pageposts as $post){
		setup_postdata($post);
			//main loop...
	}
} else {
	//未找到文章
} 
?>

这里调用了setup_postdata()函数,目的是使你可以正常使用the_title(), the_permalink()等模板标签。

通过上述四种方法,在wordpress里自定义查询就不是什么麻烦事了。

11条留言

  1. […]     至此就解决Woocommerce默认使用ID来排序分类下的产品,但有一个问题是我如果按照" menu_order" 先降序然后再按照ID来降序这种通过多个字段来进行排序的话好像不行 在WordPress-> WP_Query类里好像不能按照多个字段来排序( Woocommerce最后是调用 WP_Query来获取数据的 ), 如果出现多个排序字段, WP_Query默认使用" post_date"来排序 后来通过 solagirl 知道wp4.0以后的版本已经支持多个排序字段了,但4.0以下版本不行,看来只能通过类似$wpdb这种方式来自己写Sql进行排序了    参考:                WordPress WP_Query方法                自定义WordPress查询的4种方法                A more powerful ORDER BY in WordPress 4.0                woocommerce Docs Sorting Products by Custom Meta Fields                来自为知笔记(Wiz) […]

  2. 楼主你好
    关于使用WP_Query 来生成多个排序字段该如何处理呢?
    类似这样的”select *from nf_posts order by menu_order desc,id desc “

    1. 文档http://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters
      里面这样写的
      Multiple ‘orderby’ values using an array

      $query = new WP_Query( array( 'orderby' => array( 'title' => 'DESC', 'menu_order' => 'ASC' ) ) );
      

      产生的结果是ORDER BY post_title DESC, menu_order ASC

      WordPress 4.0后正好支持你要的东西,具体你可以看下这个介绍
      https://make.wordpress.org/core/2014/08/29/a-more-powerful-order-by-in-wordpress-4-0/

      1. 非常感谢
        不过我使用的版本是3.8,而且升级的话可能导致其他问题
        看来4.0以下的版本只能通过$wpdb Object这种方式解决了
        再次感谢

        1. 是的,用$wpdb输出结构时跟WP_Query区别不太大。

        2. 另外,如果你的两个字段都是按相同顺序排列,也可以用这种啊
          $query = new WP_Query( array ( ‘orderby’ => ‘menu_order title’, ‘order’ => ‘DESC’ ) );
          这个貌似一直支持。

          1. hi,solagirl 这种方式我在WP_Query文档里已经看到并测试过
            下面是生成的Sql,可以看到第一个默认是ASC的,但我想要的是DESC
            所以你说的”两个字段都是按相同顺序排列“是错误的,因为最后一个是按照order来排序的,其他是没有的在Mysql里默认是ASC
            SELECT SQL_CALC_FOUND_ROWS Htl_posts.ID,menu_order FROM Htl_posts WHERE 1=1
            AND Htl_posts.post_type = ‘product’
            AND (Htl_posts.post_status = ‘publish’ OR Htl_posts.post_status = ‘private’)
            ORDER BY Htl_posts.menu_order,Htl_posts.post_title DESC
            LIMIT 0, 10

            还是非常感谢楼主!

        3. 多谢指正,学习了

  3. 需要查看的就是这篇日志啦,记录下~

  4. […] 加载主题模板文件发生在最后阶段,此阶段中不管是插件的代码还是functions.php中的代码都已执行,这样我们就不奇怪为什么在single.php中调用query_posts()会增加查询次数,query_posts()大约在pre_get_posts的位置就执行完了,等程序执行到single.php时,如果调用query_posts,只能推翻前面的结果重新查一遍。我们还能看出,避免这个问题的方法就是在functions.php中使用filters函数(posts_join, posts_groupby等)更改query_posts的查询参数,因为functions.php早于query_posts执行,方法可以参考《自定义WordPress查询的4种方法》中的第三种方法。 […]

评论功能已关闭