WordPress.org

Make WordPress Core

Opened 5 years ago

Closed 5 years ago

#12731 closed enhancement (wontfix)

Add a callback to get_posts() in /wp-includes/query.php to enable more robust plugins

Reported by: mikeschinkel Owned by: ryan
Milestone: Priority: normal
Severity: normal Version: 3.0
Component: Query Keywords: 2nd-opinion
Focuses: Cc:

Description


Suggestion:

Add callback functionality to get_posts() in /wp-includes/post.php to enable more robust plugins. Allow a developer to specify a hook that when defined will process all hooks used by get_posts() and thus can allow a developer to avoid all side-effects from other plugins and fully control get_posts for specific use-cases like Widgets or other things.

With the advent of custom post types in the admin user interface there is likely to be a lot greater need for custom queries and so this will become an increasing problem.


Background:

One of the things I struggle with frequently is trying to perform a WP_Query() in a plugin or a theme only to find that some other plugin has added hooks (like a 'post_join') that cause my code to break. Of course the plugin developer didn't test against my code, and as I can't test against all other plugins my code gets trampled and my client gets upset. I really want the ability to call a query and make certain that no other plugin will create a global "side-effect" condition that can cause my plugin to fail.


Implementation:

The attached patch is but one proposed implementation to solve the problem. Addressing the issue is more important than this implementation but the following explains why the implementation was chosen:

-- Adds a $_get_post_callback instance variable to WP_Query() to hold the name of the callback.

-- Tests for "callback" in the query and if found looks for a hook that matches "get_posts_callback_$_get_posts_callback"

-- Adds the "internal" functions _do_action_ref_array_or_callback($tag), _do_action_or_callback($tag,$value) and _apply_filters_ref_array_or_callback($tag,$args) to be called as proxy for do_action_ref_array(), do_action() and apply_filters_ref_array().

-- If a valid callback exists it will be used to process all hooks within get_posts(), actions and filters, by passing a 3rd "$tag" parameter that allows the developer to decide which filters to wake on and which to ignore (this required the first parameter be echoed as the return value, unless it needs to be modified, of course.)

-- This callback will bypass all existing hooks but the plugin/theme developer can delegate back to said hooks if need be.

-- This approach was chosen instead of adding for 35 additional callback specific hooks to minimize the overhead added by this patch. As is there this patch contributes only a negligible overhead, overhead which could be recovered by a few simply optimizations of the existing get_posts() function.


Benefits:

The primary benefits are to make it much easier to get a plugin or theme that uses a custom query to work and to ensure it will continue to work no matter what other plugins are added.


Example Use Case:

Here is an example use case that adds taxonomy terms string to the query in the form, i.e. "tax1/term1,tax2/term2,...taxN/termN" (this use case and the exact code came from functionality I needed for building a website using custom post types. It is a "real world" and "in the wild" need, not a hypothetical):

  add_filter('get_posts_callback_add_tax_terms','my_add_tax_terms',10,3);
  $q = new WP_Query('post_type=session&posts_per_page=9999&callback=add_taxonomy_terms');
  foreach($q->posts as $post) {
    echo "{$post->post_title}: {$post->taxonomy_terms}\n";
  }
	
function my_add_tax_terms( $value,$query,$tag ) {
  global $wpdb;
  switch($tag) {
    case 'posts_fields':
      $value .= ',taxonomy_terms.taxonomy_terms';
      break;
    case 'posts_join':
      $value .= " LEFT OUTER JOIN (SELECT $wpdb->posts.ID AS post_ID,
        GROUP_CONCAT(CONCAT_WS('/',$wpdb->term_taxonomy.taxonomy,$wpdb->terms.slug)
        ORDER BY $wpdb->terms.name SEPARATOR ',') 
          AS taxonomy_terms FROM $wpdb->posts
        LEFT OUTER JOIN $wpdb->term_relationships ON 
          $wpdb->term_relationships.object_id=$wpdb->posts.ID
        LEFT OUTER JOIN $wpdb->term_taxonomy ON
          $wpdb->term_taxonomy.term_taxonomy_id=$wpdb->term_relationships.term_taxonomy_id
        LEFT OUTER JOIN $wpdb->terms ON 
          $wpdb->terms.term_id=$wpdb->term_taxonomy.term_id
        WHERE 1=1 GROUP BY $wpdb->posts.ID) taxonomy_terms 
          ON $wpdb->posts.ID=taxonomy_terms.post_ID ";
      break;
  }
return $value;
}

Attachments (1)

get_posts_callback_in_query.php.diff (11.7 KB) - added by mikeschinkel 5 years ago.
Patch for /wp-includes/query.php to add callback functionality as an alternate to existing hooks for get_posts().

Download all attachments as: .zip

Change History (9)

@mikeschinkel5 years ago

Patch for /wp-includes/query.php to add callback functionality as an alternate to existing hooks for get_posts().

comment:1 @mikeschinkel5 years ago

DAMN, that title should be:

Add a callback to get_posts() in /wp-includes/query.php to enable more robust plugins

comment:2 follow-up: @TobiasBg5 years ago

  • Keywords posts get_posts query removed
  • Summary changed from Add a callback to get_posts() in /wp-includes/post.php to enable more robust plugins to Add a callback to get_posts() in /wp-includes/query.php to enable more robust plugins

Well, then just change the title, I guess :-) (It's called "Summary" here.)

comment:3 in reply to: ↑ 2 @mikeschinkel5 years ago

  • Cc mikeschinkel@… added

Replying to TobiasBg:

Well, then just change the title, I guess :-) (It's called "Summary" here.)

Ah, thanks. It's very late and I just didn't see that I could edit it. Too bleary eyed from getting it all working. :)

Question: why did you remove the keywords I used?

comment:4 @TobiasBg5 years ago

No problem :-)

Keywords here in Trac have special meanings to the devs (see this list), as they describe necessary actions to be taken in the lifecycle of the ticket. They are not search keywords as they are unrelated to the actual content of a ticket.

comment:5 @mikeschinkel5 years ago

Ah, thanks.

comment:6 @mikeschinkel5 years ago

I just noticed a bug in my example use-case:

  add_filter('get_posts_callback_add_tax_terms','my_add_tax_terms',10,3);
  $q = new WP_Query('post_type=session&posts_per_page=9999&callback=add_tax_terms');
  foreach($q->posts as $post) {
    echo "{$post->post_title}: {$post->taxonomy_terms}\n";
  }
	
function my_add_tax_terms( $value,$query,$tag ) {
  global $wpdb;
  switch($tag) {
    case 'posts_fields':
      $value .= ',taxonomy_terms.taxonomy_terms';
      break;
    case 'posts_join':
      $value .= " LEFT OUTER JOIN (SELECT $wpdb->posts.ID AS post_ID,
        GROUP_CONCAT(CONCAT_WS('/',$wpdb->term_taxonomy.taxonomy,$wpdb->terms.slug)
        ORDER BY $wpdb->terms.name SEPARATOR ',') 
          AS taxonomy_terms FROM $wpdb->posts
        LEFT OUTER JOIN $wpdb->term_relationships ON 
          $wpdb->term_relationships.object_id=$wpdb->posts.ID
        LEFT OUTER JOIN $wpdb->term_taxonomy ON
          $wpdb->term_taxonomy.term_taxonomy_id=$wpdb->term_relationships.term_taxonomy_id
        LEFT OUTER JOIN $wpdb->terms ON 
          $wpdb->terms.term_id=$wpdb->term_taxonomy.term_id
        WHERE 1=1 GROUP BY $wpdb->posts.ID) taxonomy_terms 
          ON $wpdb->posts.ID=taxonomy_terms.post_ID ";
      break;
  }
return $value;
}

comment:7 @nacin5 years ago

  • Keywords 2nd-opinion added

comment:8 @scribu5 years ago

  • Milestone Awaiting Review deleted
  • Resolution set to wontfix
  • Status changed from new to closed

I think #15063 is a better idea.

Note: See TracTickets for help on using tickets.