编辑“WordPress:Custom Queries”
该编辑可以被撤销。 请检查下面的对比以核实您想要撤销的内容,然后发布下面的更改以完成撤销。
最后版本 | 您的文本 | ||
第1行: | 第1行: | ||
__TOC__ | __TOC__ | ||
Plugins generally extend the functionality of WordPress by adding [[WordPress:Plugin API|Hooks]] (Actions and Filters) that change the way WordPress behaves. But sometimes a plugin needs to go beyond basic hooks by doing a custom query, and it's not as simple as just adding one filter or action to WordPress. This article describes what custom queries are, and then explains how a plugin author can implement them. | Plugins generally extend the functionality of WordPress by adding [[WordPress:Plugin API|Hooks]] (Actions and Filters) that change the way WordPress behaves. But sometimes a plugin needs to go beyond basic hooks by doing a custom query, and it's not as simple as just adding one filter or action to WordPress. This article describes what custom queries are, and then explains how a plugin author can implement them. | ||
A few notes: | A few notes: | ||
* This article assumes you are already familiar with the basics of [[WordPress:Writing a Plugin]], as well as [[WordPress:Creating Tables with Plugins]] (if applicable for your plugin), the [[WordPress:Plugin API]] for Actions and Filters, PHP, and the MySQL database query language. | * This article assumes you are already familiar with the basics of [[WordPress:Writing a Plugin]], as well as [[WordPress:Creating Tables with Plugins]] (if applicable for your plugin), the [[WordPress:Plugin API]] for Actions and Filters, PHP, and the MySQL database query language. | ||
* This article applies only to the viewer-facing blog pages, not the administration screens (although some of what you do may affect administration screens that lists posts as well). | * This article applies only to the viewer-facing blog pages, not the administration screens (although some of what you do may affect administration screens that lists posts as well). | ||
* All file names mentioned are relative to the root WordPress directory. | * All file names mentioned are relative to the root WordPress directory. | ||
== Background Information == | == Background Information == | ||
=== Definitions === | === Definitions === | ||
In the context of this article, '''query''' refers to the database query that WordPress uses in the Loop to find the list of posts that are to be displayed on the screen ("database query" will be used in this article to refer to generic database queries). By default, the WordPress query searches for posts that belong on the currently-requested page, whether it is a single post, single static page, category archive, date archive, search results, feed, or the main list of blog posts; the query is limited to a certain maximum number of posts (set in the Options admin screens), and the posts are retrieved in reverse-date order (most recent post first). A plugin can use a custom query to override this behavior. Examples: | In the context of this article, '''query''' refers to the database query that WordPress uses in the Loop to find the list of posts that are to be displayed on the screen ("database query" will be used in this article to refer to generic database queries). By default, the WordPress query searches for posts that belong on the currently-requested page, whether it is a single post, single static page, category archive, date archive, search results, feed, or the main list of blog posts; the query is limited to a certain maximum number of posts (set in the Options admin screens), and the posts are retrieved in reverse-date order (most recent post first). A plugin can use a custom query to override this behavior. Examples: | ||
* Display posts in a different order, such as alphabetically for a "glossary" category. | * Display posts in a different order, such as alphabetically for a "glossary" category. | ||
* Override the default number of posts to be displayed on the page; for example, the glossary plugin might want to have a higher post limit when displaying the glossary category. | * Override the default number of posts to be displayed on the page; for example, the glossary plugin might want to have a higher post limit when displaying the glossary category. | ||
* Exclude certain posts from certain pages; for example, posts from the glossary category might be excluded from the home page and archive pages, and appear only on their own category page. | * Exclude certain posts from certain pages; for example, posts from the glossary category might be excluded from the home page and archive pages, and appear only on their own category page. | ||
* Expand the default WordPress keyword search (which normally just searches in post title and content) to search in other fields, such as the city, state, and country fields of a geographical tagging plugin. | * Expand the default WordPress keyword search (which normally just searches in post title and content) to search in other fields, such as the city, state, and country fields of a geographical tagging plugin. | ||
* Allow custom URLs such as <tt>example.com/blog?geostate=oregon</tt> or <tt>example.com/blog/geostate/oregon</tt> to refer to the archive of posts tagged with the state of Oregon. | * Allow custom URLs such as <tt>example.com/blog?geostate=oregon</tt> or <tt>example.com/blog/geostate/oregon</tt> to refer to the archive of posts tagged with the state of Oregon. | ||
=== Default WordPress Behavior === | === Default WordPress Behavior === | ||
Before you try to change the default behavior of queries in WordPress, it is important to understand what WordPress does by default. There is an overview of the process WordPress uses to build your blog pages, and what a plugin can do to modify this behavior, in [[WordPress:Query Overview]]. | Before you try to change the default behavior of queries in WordPress, it is important to understand what WordPress does by default. There is an overview of the process WordPress uses to build your blog pages, and what a plugin can do to modify this behavior, in [[WordPress:Query Overview]]. | ||
== Implementing Custom Queries == | == Implementing Custom Queries == | ||
Now we're ready to start actually doing some custom queries! This section of the article will use several examples to demonstrate how query modification can be implemented. We'll start with a simple example, and move on to more complex ones. | Now we're ready to start actually doing some custom queries! This section of the article will use several examples to demonstrate how query modification can be implemented. We'll start with a simple example, and move on to more complex ones. | ||
=== Display Order and Post Count Limit === | === Display Order and Post Count Limit === | ||
For our first example, let's consider a glossary plugin that will let the site owner put posts in a specific "glossary" category (saved by the plugin in global variable <tt>$gloss_category</tt>). When someone is viewing the glossary category, we want to see the entries alphabetically rather than by date, and we want to see all the glossary entries, rather than the number chosen by the site owner in the options. | For our first example, let's consider a glossary plugin that will let the site owner put posts in a specific "glossary" category (saved by the plugin in global variable <tt>$gloss_category</tt>). When someone is viewing the glossary category, we want to see the entries alphabetically rather than by date, and we want to see all the glossary entries, rather than the number chosen by the site owner in the options. | ||
So, we need to modify the query in two ways: | So, we need to modify the query in two ways: | ||
# Add a filter to the <tt>ORDER BY</tt> clause of query to change it to alphabetical order if we are viewing the glossary category. The name of the filter is <tt>'posts_orderby'</tt>, and it filters the text after the words <tt>ORDER BY</tt> in the SQL statement. | # Add a filter to the <tt>ORDER BY</tt> clause of query to change it to alphabetical order if we are viewing the glossary category. The name of the filter is <tt>'posts_orderby'</tt>, and it filters the text after the words <tt>ORDER BY</tt> in the SQL statement. | ||
# Add a filter to the <tt>LIMIT</tt> clause of the query to remove the limit. This filter is called <tt>'post_limits'</tt>, and it filters the SQL text for limits, including the <tt>LIMIT</tt> key word. | # Add a filter to the <tt>LIMIT</tt> clause of the query to remove the limit. This filter is called <tt>'post_limits'</tt>, and it filters the SQL text for limits, including the <tt>LIMIT</tt> key word. | ||
In both cases, the filter function will only make these modifications if we're viewing the glossary category (function <tt>is_category</tt> is used for that logic). So, here's what we need to do: | In both cases, the filter function will only make these modifications if we're viewing the glossary category (function <tt>is_category</tt> is used for that logic). So, here's what we need to do: | ||
<pre> | <pre> | ||
第123行: | 第65行: | ||
} | } | ||
</pre> | </pre> | ||
=== Category Exclusion === | === Category Exclusion === | ||
To continue with the glossary plugin, we also want to exclude glossary entries from appearing on certain screens (home, non-category archives) and feeds. To do this, we will add a <tt>'pre_get_posts'</tt> action that will detect what type of screen was requested, and depending on the screen, exclude the glossary category. We'll also use the fact that in the query specification (which is stored in <tt>$wp_query->query_vars</tt>, see above), you can put a "-" sign before a category index number to exclude that category. So, here is the code: | To continue with the glossary plugin, we also want to exclude glossary entries from appearing on certain screens (home, non-category archives) and feeds. To do this, we will add a <tt>'pre_get_posts'</tt> action that will detect what type of screen was requested, and depending on the screen, exclude the glossary category. We'll also use the fact that in the query specification (which is stored in <tt>$wp_query->query_vars</tt>, see above), you can put a "-" sign before a category index number to exclude that category. So, here is the code: | ||
<pre> | <pre> | ||
第183行: | 第87行: | ||
</pre> | </pre> | ||
=== Keyword Search in Plugin Table === | === Keyword Search in Plugin Table === | ||
For our next example, let's consider a geographical tagging plugin that tags each post with one or more cities, states, and countries. The plugin stores them in its own database table; we'll assume the table name is in global variable <tt>$geotag_table</tt>, and that it has fields <tt>geotag_post_id, geotag_city, geotag_state, geotag_country</tt>. For this example, the idea is that if someone does a keyword search (which normally searches the post title and post content), we also want to find posts where the keyword appears in the city, state, or country fields of our plugin's table. | For our next example, let's consider a geographical tagging plugin that tags each post with one or more cities, states, and countries. The plugin stores them in its own database table; we'll assume the table name is in global variable <tt>$geotag_table</tt>, and that it has fields <tt>geotag_post_id, geotag_city, geotag_state, geotag_country</tt>. For this example, the idea is that if someone does a keyword search (which normally searches the post title and post content), we also want to find posts where the keyword appears in the city, state, or country fields of our plugin's table. | ||
So, we are going to need to modify the SQL query used to find posts in several ways (but only if we're on a search screen): | So, we are going to need to modify the SQL query used to find posts in several ways (but only if we're on a search screen): | ||
* Join the plugin's table to the post table. This is done with the <tt>'posts_join'</tt> filter, which acts on the SQL JOIN clause(s). | * Join the plugin's table to the post table. This is done with the <tt>'posts_join'</tt> filter, which acts on the SQL JOIN clause(s). | ||
* Expand the WHERE clause of the query to look in the plugin table fields. This is done with the <tt>'posts_where'</tt> filter, and we'll use the idea that whatever WordPress did to search the post title field, we'll do the same thing with our custom table fields (rather than trying to duplicate WordPress's rather complex logic). WordPress adds clauses like this: <tt>(post_title LIKE 'xyz')</tt>. | * Expand the WHERE clause of the query to look in the plugin table fields. This is done with the <tt>'posts_where'</tt> filter, and we'll use the idea that whatever WordPress did to search the post title field, we'll do the same thing with our custom table fields (rather than trying to duplicate WordPress's rather complex logic). WordPress adds clauses like this: <tt>(post_title LIKE 'xyz')</tt>. | ||
* Add a GROUP BY clause to the query, so that, for instance, if a post is tagged with both Portland, Oregon, and Salem, Oregon, and the viewer searches on "Oregon", we won't end up returning the same post twice. This is done with the <tt>'posts_groupby'</tt> filter, which acts on the text after the words GROUP BY in the SQL statement. | * Add a GROUP BY clause to the query, so that, for instance, if a post is tagged with both Portland, Oregon, and Salem, Oregon, and the viewer searches on "Oregon", we won't end up returning the same post twice. This is done with the <tt>'posts_groupby'</tt> filter, which acts on the text after the words GROUP BY in the SQL statement. | ||
With those ideas in mind, here is the code: | With those ideas in mind, here is the code: | ||
<pre> | <pre> | ||
第298行: | 第151行: | ||
// wasn't empty, append ours | // wasn't empty, append ours | ||
return $groupby . ", " . $mygroupby; | return $groupby . ", " . $mygroupby; | ||
} | } | ||
第376行: | 第157行: | ||
=== Custom Archives === | === Custom Archives === | ||
To continue with the geo-tagging plugin from the last example, let's assume we want the plugin to enable custom permalinks of the form <tt>www.example.com/blog?geostate=oregon</tt> to tell WordPress to find posts whose state matches "oregon" and display them. | To continue with the geo-tagging plugin from the last example, let's assume we want the plugin to enable custom permalinks of the form <tt>www.example.com/blog?geostate=oregon</tt> to tell WordPress to find posts whose state matches "oregon" and display them. | ||
To get this to work, the plugin must do the following: | To get this to work, the plugin must do the following: | ||
* Ensure that when WordPress parses the URL, the state gets saved in the query variables; to do this, we have to add "geostate" to the list of query variables WordPress understands (<tt>query_vars</tt> filter). Here's how to do that: | * Ensure that when WordPress parses the URL, the state gets saved in the query variables; to do this, we have to add "geostate" to the list of query variables WordPress understands (<tt>query_vars</tt> filter). Here's how to do that: | ||
<pre> | <pre> | ||
add_filter('query_vars', 'geotag_queryvars' ); | add_filter('query_vars', 'geotag_queryvars' ); | ||
第411行: | 第171行: | ||
} | } | ||
</pre> | </pre> | ||
* Do the right query when the "geostate" query variable is found; this is similar to the custom queries discussed in the previous examples. The only difference is that instead of testing for <tt>is_search</tt> or something similar in <tt>posts_where</tt> and other database query filters, we'll instead test to see whether the "geostate" query variable has been detected. Here is the code to replace the <tt>if( is_search() )</tt> statements in the examples above: | * Do the right query when the "geostate" query variable is found; this is similar to the custom queries discussed in the previous examples. The only difference is that instead of testing for <tt>is_search</tt> or something similar in <tt>posts_where</tt> and other database query filters, we'll instead test to see whether the "geostate" query variable has been detected. Here is the code to replace the <tt>if( is_search() )</tt> statements in the examples above: | ||
<pre> | <pre> | ||
global $wp_query; | global $wp_query; | ||
第433行: | 第181行: | ||
* Probably the plugin also needs to generate these permalinks. For instance, the plugin might have a function called <tt>geotags_list_states</tt> that would find out which states exist in its geotag table, and make links to them: | * Probably the plugin also needs to generate these permalinks. For instance, the plugin might have a function called <tt>geotags_list_states</tt> that would find out which states exist in its geotag table, and make links to them: | ||
<pre> | <pre> | ||
function geotags_list_states( $sep = ", " ) | function geotags_list_states( $sep = ", " ) | ||
第464行: | 第209行: | ||
} | } | ||
</pre> | </pre> | ||
=== Permalinks for Custom Archives === | === Permalinks for Custom Archives === | ||
If the blog user has non-default [[WordPress:Using Permalinks|permalinks]] enabled, we can go one step further in the previous custom archives example, and enable the URL <tt>example.com/blog/geostate/oregon</tt> to also list all posts tagged with the state of Oregon. To do this, we add to WordPress's "rewrite rules", which basically tell WordPress how to interpret permalink-style URLs. Specifically, we add a rewrite rule that tells WordPress to interpret <tt>/geostate/oregon</tt> URLs the same as <tt>?geostate=oregon</tt>. (See [[WordPress:Query Overview]] for more information on the rewrite process.) | If the blog user has non-default [[WordPress:Using Permalinks|permalinks]] enabled, we can go one step further in the previous custom archives example, and enable the URL <tt>example.com/blog/geostate/oregon</tt> to also list all posts tagged with the state of Oregon. To do this, we add to WordPress's "rewrite rules", which basically tell WordPress how to interpret permalink-style URLs. Specifically, we add a rewrite rule that tells WordPress to interpret <tt>/geostate/oregon</tt> URLs the same as <tt>?geostate=oregon</tt>. (See [[WordPress:Query Overview]] for more information on the rewrite process.) | ||
In practice, to define a new rewrite rule, there are two steps: (1) "flush" the cached rewrite rules using an <tt>init</tt> filter, to force WordPress to recalculate the rewrite rules, and (2) use the <tt>generate_rewrite_rules</tt> action to add a new rule when they are calculated. Here's the "flush" code: | In practice, to define a new rewrite rule, there are two steps: (1) "flush" the cached rewrite rules using an <tt>init</tt> filter, to force WordPress to recalculate the rewrite rules, and (2) use the <tt>generate_rewrite_rules</tt> action to add a new rule when they are calculated. Here's the "flush" code: | ||
<pre> | <pre> | ||
add_action('init', 'geotags_flush_rewrite_rules'); | add_action('init', 'geotags_flush_rewrite_rules'); | ||
第531行: | 第226行: | ||
The rule generation is slightly more complex. Basically, the rewrite rules array is an associative array whose keys are regular expressions that match potential permalink URLs, and whose values are the corresponding non-permalink-style URLs they correspond to. So, to define a rewrite rule that matches URLs like <tt>/geostate/oregon</tt> (with arbitrary states), and tells WordPress it should correspond to <tt>?geostate=oregon</tt>, we do the following: | The rule generation is slightly more complex. Basically, the rewrite rules array is an associative array whose keys are regular expressions that match potential permalink URLs, and whose values are the corresponding non-permalink-style URLs they correspond to. So, to define a rewrite rule that matches URLs like <tt>/geostate/oregon</tt> (with arbitrary states), and tells WordPress it should correspond to <tt>?geostate=oregon</tt>, we do the following: | ||
<pre> | <pre> | ||
add_action('generate_rewrite_rules', 'geotags_add_rewrite_rules'); | add_action('generate_rewrite_rules', 'geotags_add_rewrite_rules'); |