Skip to content

Instantly share code, notes, and snippets.

@bonny
Last active May 9, 2016 18:50
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bonny/5011943 to your computer and use it in GitHub Desktop.
Save bonny/5011943 to your computer and use it in GitHub Desktop.
WordPress function with_posts(), that simplifies getting and looping posts, in a jQuery anonymous function-ish way. Setups global, restores post afterwards. Simple, flexible and powerful.
<?php
/**
* WordPress WP_QUERY-wrapper to simplify getting and working with posts
*
* Does something with posts, using a callback
* Setups global post variable before running callback
* And restores it afterwards
*
* An introductionary blogpost about this function is posted on our blog:
* http://labs.earthpeople.se/2013/02/wordpress-loops-with_posts-is-my-way-of-dealing-with-them/
*
* Examples:
* https://gist.github.com/bonny/5005579
*
* Link to orginal GIST:
* https://gist.github.com/bonny/5011943
*
* @author Pär Thernstrom <https://twitter.com/eskapism>
* @param ID, array, string, WP POST, WP_QUERY
* @param $do callable Function to run for each matching post
* @param bool $buffer_and_return_output True if output should be buffered and returned
* @return Mixed Returns the return value of the $do function
*/
function with_posts($post_thing, $do, $buffer_and_return_output = FALSE) {
if ( ! is_callable( $do ) ) return FALSE;
// Set defaults
$wp_query_args = array(
"post_status" => "publish",
"posts_per_page" => -1,
"orderby" => "date",
"order" => "DESC",
);
// Get all public custom post types and add to query args
$get_post_types_args = array(
"public" => TRUE
);
$post_types = get_post_types( $get_post_types_args, $output = 'names');
$wp_query_args["post_type"] = array_keys($post_types);
$posts_query = NULL;
$callback_return = NULL;
$buffered_output = NULL;
$found_valid_post_thing = FALSE;
if ( is_numeric( $post_thing ) ) {
// If post_thing is numeric then get the post with that id
$wp_query_args["post__in"] = array( (int) $post_thing);
$found_valid_post_thing = TRUE;
} elseif ( is_string( $post_thing ) ) {
// If post_thing is a string,
// check if it's a wp_query compatible string with args,
// or simply a comma separated list of ids.
// or none of that.
// compatible format is like: 'post_type=regions&posts_per_page=3&orderby=title&order=asc
parse_str($post_thing, $arr_parsed_thing);
if ( is_null( $arr_parsed_thing ) || ! is_array( $arr_parsed_thing ) ) {
// Something went bananas
break;
}
// If size is just one, and key contains commas, and value is empty,
// then this looks like a comma separated list of id's
// or it could be a non-integer string = get post by path/slug/post_name
if ( sizeof( $arr_parsed_thing ) === 1 ) {
reset($arr_parsed_thing);
$first_key = key($arr_parsed_thing);
if ( $arr_parsed_thing[ $first_key ] === "" && strpos( $first_key , ",") !== FALSE ) {
// If post_thing is a comma separated string then get the posts, in the order they are in the string
// First check for numeric
$arr_post_vals = explode(",", $first_key);
/*
Example all strings
Array
(
[0] => nickelodeon
[1] => se
[2] => fi
[3] => punkd
)
Example all integers
Array
(
[0] => 1
[1] =>
[2] => 2
[3] => 3
[4] => 5
[5] => 993
)
*/
// Remove empty vals from array
$arr_post_vals = array_filter($arr_post_vals);
// Check if array only is integers
$found_only_integers = TRUE;
foreach ($arr_post_vals as $one_val) {
if ( ! is_numeric($one_val) ) {
$found_only_integers = FALSE;
break;
}
}
$arr_post_ids = NULL;
// If not only integers, then assume post_slugs
// So quickly fetch the ids of matching pages
if ( FALSE === $found_only_integers ) {
// Match post things like:
// with_posts(",nickelodeon,se,fi,,punkd,,hepp,hopp,,"
global $wpdb;
$arr_sql_in = array();
foreach ( $arr_post_vals as $one_val ) {
$arr_sql_in[] = $wpdb->prepare("%s", $one_val);
}
$sql_in = implode(",", $arr_sql_in);
$sql_in = "( $sql_in )";
$sql = "SELECT ID from $wpdb->posts WHERE post_name IN $sql_in";
$results = $wpdb->get_results( $sql, "OBJECT_K" );
$arr_post_ids = array_keys( $results );
} else {
// Matched post thing like
// with_posts("1,,2,3,5,993,5634,"
$arr_post_ids = $arr_post_vals;
}
$wp_query_args["post__in"] = $arr_post_ids;
$wp_query_args["orderby"] = "post__in";
$found_valid_post_thing = TRUE;
} else if ( $arr_parsed_thing[ $first_key ] === "" ) {
// get post by slug.
// Could we use get_page_by_path here for some reason? Would that improve anything?
$wp_query_args["name"] = $first_key;
}
}
// If still not found valid thing, it wasn't a comma separated list
// So let's go with wp_query_args instead
if ( ! $found_valid_post_thing ) {
$wp_query_args = wp_parse_args($arr_parsed_thing , $wp_query_args);
$found_valid_post_thing = TRUE;
}
} elseif ( is_object( $post_thing) && get_class( $post_thing ) === "WP_Post" ) {
// If post_thing is a WP_Post-object, like the one you get when using get_post()
$wp_query_args["post__in"] = array( (int) $post_thing->ID );
$found_valid_post_thing = TRUE;
} elseif ( is_object( $post_thing ) && get_class( $post_thing ) === "WP_Query" ) {
// If post_thing is wp_query object
// Then just use it
$posts_query = $post_thing;
$found_valid_post_thing = TRUE;
}
// We're getting called with something we don't support
if (FALSE === $found_valid_post_thing) {
_doing_it_wrong( __FUNCTION__, 'You passed something to me that I don\'t understand', '3.5' );
return FALSE;
}
if ($buffer_and_return_output === TRUE) {
ob_start();
}
if ( is_null( $posts_query ) ) $posts_query = new wp_query($wp_query_args);
if ( $posts_query->have_posts() ) {
$arr_return_to_callback = array(
"post_count" => NULL,
"current_post" => NULL,
"post" => NULL,
"wp_query" => $posts_query,
);
while( $posts_query->have_posts() ) :
$posts_query->the_post();
$arr_return_to_callback["post_count"] = $posts_query->post_count;
$arr_return_to_callback["current_post"] = $posts_query->current_post;
$arr_return_to_callback["post"] = $posts_query->post;
// Run callback, for each post
// Also include some nice and useful stuff
// the current post is the first argument, then an array with all other info
$callback_return = call_user_func( $do, $arr_return_to_callback["post"], $arr_return_to_callback);
endwhile;
}
wp_reset_postdata();
if ($buffer_and_return_output === TRUE) {
$buffered_output = ob_get_clean();
$posts_query->buffered_output = $buffered_output;
}
// Return the posts_query we used
return $posts_query;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment