One question I come across a lot regarding custom fields is how to only retrieve posts based on a custom field.
For example, if a post has a custom field of “MyData”, someone might want to only retrieve that particular post.
The WordPress Codex has a technique for retrieving posts based on custom fields, which consists of writing your own query and going through the results.
The technique in the Codex is good, but I’ve found a re-usable way one can retrieve only posts with certain custom fields.
The technique I use makes use of two custom functions placed in a theme’s “functions.php” and a custom WordPress Loop.
Let’s get started — The “functions.php” file
First, let’s place the two custom functions in the “functions.php” file. This file should be in your theme directory, but if it isn’t there, you can create one using any text editor.
Here are the two functions below:
function get_custom_field_posts_join($join) { global $wpdb, $customFields; return $join . " JOIN $wpdb->postmeta postmeta ON (postmeta.post_id = $wpdb->posts.ID and postmeta.meta_key in ($customFields)) "; } function get_custom_field_posts_group($group) { global $wpdb; $group .= " $wpdb->posts.ID "; return $group; }
The function “get_custom_field_posts_join” makes use of an advanced WordPress filter called “posts_join“. Each time posts are called, you can add on extra MySQL parameters using filters. In this case, I add on an option to find certain postmeta. Please note the use of a global variable called “customFields“, which I’ll explain a bit later.
The function “get_custom_field_posts_group” makes use of another advanced WordPress filter called “posts_group“. This is used to avoid duplicate entries in our return query.
Now Let’s Work on Our Loop
After the two functions are placed in the “functions.php” file, it’s time to work on placing the appropriate code into one of our template files.
For this example, I’ll be modifying the “sidebar.php” file in the WordPress default theme. You could place the below code in any of your theme files, however.
<?php /* Begin Custom Field Posts */ ?> <h2>Custom Posts</h2> <ul> <?php global $customFields; $customFields = "'Links', 'MyData'"; //Comma seperated 's1', 's2', 's3'
The first part of the code deals with establishing the structure of the output. You really could do anything you want here.
Please note the use of the global variable “customFields“. What we’re doing here is setting up a comma-separated variable that will be used to search for our custom fields. The “customFields” variable is used in the “get_custom_field_posts_join” function.
In this example, it is assumed we want to find posts with custom fields of “Links” and “MyData“.
Custom Fields – Links and MyData
The next bit of code instantiates a new instance of WP_Query and runs a query to return some posts.
$customPosts = new WP_Query(); add_filter('posts_join', 'get_custom_field_posts_join'); add_filter('posts_groupby', 'get_custom_field_posts_group'); $customPosts->query('showposts=5' );//Uses same parameters as query_posts remove_filter('posts_join', 'get_custom_field_posts_join'); remove_filter('posts_groupby', 'get_custom_field_posts_group');
Note the use of the “add_filter” and “remove_filter” functions. Since we are doing a post query, we can tap into the query and add our own parameters. So before the query is initiated, two query-type filters are added, and after the query is initiated, the two filters are deactivated since we only want them run once.
This last bit of code initiates our custom loop, gets the custom values, spits them out, and ends the loop.
while ($customPosts->have_posts()) : $customPosts->the_post(); $links = get_post_custom_values("Links"); $data = get_post_custom_values("MyData"); ?> <li><a href='<?php echo $links[0]; ?>'><?php echo $data[0]; ?></a></li> <?php endwhile; ?> </ul> <?php /* End Custom Field Posts */ ?>
The "get_post_custom_values" WordPress function returns an array of matching keys. It's assumed there is only one key per post, which is why we echo out the first value (Ex: echo $links[0]
).
The Full Post Code
Here is the full post code. The only thing you need to change for your own use is the custom fields needed (change the customFields text) and what type of output is desired within our custom loop.
<?php /* Begin Custom Field Posts */ ?> <h2>Custom Posts</h2> <ul> <?php global $customFields; $customFields = "'Links', 'MyData'"; //Comma seperated 's1', 's2', 's3' $customPosts = new WP_Query(); add_filter('posts_join', 'get_custom_field_posts_join'); add_filter('posts_groupby', 'get_custom_field_posts_group'); $customPosts->query('showposts=5' );//Uses same parameters as query_posts remove_filter('posts_join', 'get_custom_field_posts_join'); remove_filter('posts_groupby', 'get_custom_field_posts_group'); while ($customPosts->have_posts()) : $customPosts->the_post(); $links = get_post_custom_values("Links"); $data = get_post_custom_values("MyData"); ?> <li><a href='<?php echo $links[0]; ?>'><?php echo $data[0]; ?></a></li> <?php endwhile; ?> </ul> <?php /* End Custom Field Posts */ ?>
Custom Field Output
Downloadable Code
The full code mentioned in this post is available for download. Within the "zip" file are a sample "functions.php" and "sidebar.php".
Conclusion
With the above technique, you can do some pretty fancy stuff. For example, you can only retrieve posts with custom images for a nice magazine effect.
If you have any questions regarding the code, I'll do my best to answer them in the comments.
Ronald, excellent post! I haven’t had a chance to test it yet, but I look forword to giving it a try.
@John,
Good luck with it. Hopefully it works out for you.
I bow to you and your l33t mastery of WordPress Loop. I would totally do a highlander reference, but I’ll have to make sure that everyone gets it. So I’m not, but yeah “There can only be one!”
I do like the current set of articles, but think that they should also be in the Codex.
@Jacob,
By all means, go for it 🙂 – the Codex that is, not the highlander reference.
Thanks for writing this article, it seems very close to something I would like to be able to do, except with pages. Do you know of a relatively easy way to get this to work with pages instead of posts?
@Matt,
No, I haven’t played around with pages a whole lot. Perhaps you can try tapping into the ‘posts_where’ filter if you want to limit the results further.
Can anyone else help Matt with this?
very nice! it would also be handy to have something like this that matched key/value pairs (where x=y in custom fields).
Pages are just posts with a different post_type.
I haven’t tried it, but this _may_ work (I’ve only glanced at query.php):
$customPosts->query('showposts=5&post_type=page');
This only lists posts with defined custom fields. Is there any way to still append posts without custom fields? And does paging work with this solution? Thanks!
Thanks for the tip! Very helpful. I was only aware of
the_meta()
template tag which isn’t nearly as flexible asget_post_custom_values()
.@Viper,
Thanks for weighing in.
@Ekro,
This technique only returns posts just with custom fields. Paging doesn’t currently work. I’ll probably write a separate post on that.
Great post! Thanks! I’d done something similar but basically used a query object and pulled everything out that way. Is this way more efficient? On my personal site I’ve implemented something using your mechanism, but I’m wondering if it’s worth updating my other project…this way is slightly cleaner I think. Very interested to know if it’s more efficient. Thanks!
Hi…
You have many excellent articles, tutorials and posts about WordPress. Can I translate them to my native language (Bahasa Indonesia) and place them on my blog leaving the original link to you???
Thank you very much…
@Kang,
Please get in contact with Mark via the WLTC contact form. He’ll be able to answer your question.
@Dan,
This way is perhaps more reusable. More efficient? Not likely. If anything, I’d say your technique and this one are about the same. Both make use of one query. The only difference is, I modify the query for posts using built-in WordPress filters. You probably call a query directly.
Thank you so much for this post! I was trying to figure it out myself, but that didn’t work.
@Viper: that works!
Thanks a lot, I was just trying to look for something like that.
The only question – I’m really far from being an expert in coding – how can you then order the fields according to a custom field ?
Great how-to !
Is it possible to use this code also for pages with custom fields?
@Ronald – Thanks for the tutorial, its helping me to get to grips with something I’m trying to do, but I’m struggling a little. I’d like to display a list of posts where a certain custom field key has a certain value. So for example, show all posts where the key Number was equal to 10.
I can’t work out if I should change the mysql query, or if i should alter something later on like use an if statement to check for a key equaling a value.
Any help would be appreciated,
Alex
Hi, I am not a PHP coder and not any idea about it. I have a simple and only one question. Can you please tell me how would I get to know which “KEY” have been assigned on a theme for the custom fields? All themes having different KEY. Most of them “Thumbnail” or “thumbnail” or “image etc. If there is a read me file for the theme then easy to get that. But if they don’t have a readme file it’s very difficult to get the KEY for the custom field. Can you please guide me how and where I can check which KEY the coder has been assigned or coded for the Custom Fields. Again I am not good in PHP. Please email me the detail. Thanks, Tim
excellent post, very helpful, thank you.
i’d love to use the same method for my rss feed, as in only let posts thru that have a specific custom field. i’m trying to write a plugin that does this but i’m not there yet. any suggestions on that?
thanks Ronald this article work good for me.. i also apply another filter ‘posts_where’ but apparently by using this code the pagination is not working.. they won’t outside the loop. Do you know why this happen?
Hei Ronald,
I am trying to adapt your code to my need (displaying a certain custom filed from all the posts within a give category) and I am already halfway:
How can I instruct the code to display the custom fields of categeory ID=50 for example?
After I will be able to “read” the custom fields form all the posts in cat 50, I wil want to strip all the caracters and have only the numbers, and then do an average with them (ie. the average price (my custom field is a price) in cat 50 is x).
Any thougts about adapting for a certain category?
Many thanks 🙂
Puiu D. – Romania
tnx for this post…
and i have a qustion. if i ant to list my post that has no any custom field, what i do?