Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I have a custom post type 'properties' that I'd like to control the HTML output for. For simplicity, let's focus on the archive view.

As a basic example, here is what a loop looks like in an archive.php file:

<?php while ( have_posts() ) : the_post(); ?>
    <h2><?php the_title(); ?></h2>
    <div><?php the_content(); ?></div>
<?php endwhile; ?>

I'd like to be able to modify the output of the loop with my custom 'properties' HTML without adding a new template or using a shortcode - basically, without user intervention. Just to be clear, I'd like to replace the <h2> and <div> in the above example, and nothing before/after it.

Note: The above is only an example. I would like to control the loop output regardless of theme.

Right now I am using output buffering to capture the output from loop_start to loop_end and replacing it with my own, but this potentially can create conflicts with other plugins.

Is there an accepted/better way to do this?

share|improve this question
    
You could create a template in your plugin that overrides the user's template but that creates conflicts with the user's theme. Are you just trying to make it easy on the user? –  developdaly Dec 7 '12 at 19:29
    
Yes, I'm trying to make this plug-and-play and compatible with as many themes as possible. The problem with overriding a template is you can't predict all of the other HTML the theme's CSS will be relying on. That's why I'm trying to target only the loop - because I only care about the display of the CPT. –  Andy Adams Dec 7 '12 at 22:50
    
Make plugin.... –  Wyck Dec 8 '12 at 17:13
    
@andy what output do you try to control? you can try to add_action('template_redirect', 'wpse_75558_template_redirect') to call directly before headers are sent for the output of the rendered template or if you want to control the_content() output you can use add_filter( 'the_content', 'wpse_75558_the_content' ) try this WP action reference –  nackle Dec 10 '12 at 19:28
    
@nackle I'd like to control the whole output within the loop - not just the_content. The reason a template_redirect won't work is because I'd like to keep all of the HTML of the theme the same, only replacing the content within the loop - if that makes any sense! –  Andy Adams Dec 10 '12 at 22:40
add comment

3 Answers

up vote 3 down vote accepted
+100

There're two very often forgotten action ref arrays: loop_start/_end().

Just turn on output buffering and you're ready to go.

add_action( 'loop_start', 'wpse75307_plugin_loop' );
add_action( 'loop_end', 'wpse75307_plugin_loop' );
/**
 * Callback function triggered during:
 * + 'loop_start'/`have_posts()` after the last post gets rendered
 * + 'loop_end'/`the_post()` before the 1st post gets rendered
 * @param  object \WP_Query Passed by reference
 * @return 
 */
function wpse75307_plugin_loop( &$obj )
{
    # if ( is_main_query() )
    # DO STUFF ... OR DONT
    global $post;

    // Start output buffering at the beginning of the loop and abort
    if ( 'loop_start' === current_filter() )
        return ob_start();

    // At the end of the loop, we end the buffering and save into a var
    # if ( is_main_query() )
    # DO STUFF ... OR DONT
    $loop_content = ob_get_flush();

    // You can do something with $loop_content...
    // Add your own loop, or...
    // Whatever you can imagine
}

Note: I wouldn't do it like this, but as you said, you want exactly that level of overriding, here you go.

share|improve this answer
    
Ok, this is how I've been approaching it - the major concern I have is conflicting with other plugins or themes. I can check is_main_query(), but there's always the possibility of a separate plugin using the main query and then running wp_reset_query - I'm guessing there is no way around this potential issue? –  Andy Adams Dec 11 '12 at 16:33
    
You hooked into exactly those hooks? –  kaiser Dec 11 '12 at 17:02
    
Yes, I have - and I've been doing output buffering. But the issue still remains of determining if I'm really in the "main" query. Is this a non-issue in your experience? –  Andy Adams Dec 11 '12 at 17:51
1  
+1 for the forgotten actions. Never saw those ones, danke! –  brasofilo Dec 11 '12 at 19:57
1  
Thanks for confirming. I'm going to leave this open for a day or so to see if anyone has a miracle workaround, but at this point I think you've pretty much confirmed my suspicions and gave me the best potential workaround. –  Andy Adams Dec 11 '12 at 23:34
show 3 more comments

You can change the loop via hook, like this for cpt 'my_post_type '

// $this? - example was used in class-structures
// add custom post type to wp loop
add_filter( 'pre_get_posts', array( $this, 'add_to_query') );

// ads to query
function add_to_query( $query ) {

    if ( is_admin() || is_preview() )
        return;

    if ( ! isset( $query -> query_vars['suppress_filters'] ) )
        $query -> query_vars['suppress_filters'] = FALSE;

    // conditional tags for restrictions
    if ( is_home() || is_front_page() && ( FALSE == $query -> query_vars['suppress_filters'] ) ) 
        $query->set( 'post_type', array( 'post', 'my_post_type' ) );

    return $query;
}
share|improve this answer
    
This doesn't change the HTML output within the loop, which is my goal - not modifying the query results. Thank you, though. –  Andy Adams Dec 10 '12 at 22:41
add comment

If remembering well, I derived this technique from here: Use template_include with custom post types.

We use a plugin that will generate a "Virtual Template" for a given Post Type. The filter template_include will render a template file that resides in the plugin folder.

You have to refine the function custom_template and the template file to suit your needs. Hope this helps ;)


Plugin File

adjust the post_type name in the class instantiation

<?php
! defined( 'ABSPATH' ) AND exit;
/*
Plugin Name: Virtual Template for CPT
Plugin URI: http://wordpress.stackexchange.com/q/75307/12615
Description: Use the plugin's template file to render an outsider loop.
Author: brasofilo
Author URI: http://wordpress.stackexchange.com/users/12615/brasofilo
Version: 2012.12.11
License: GPLv2
*/

$virtual_template = new VirtualTemplateForCPT_class( 'movies' );

class VirtualTemplateForCPT_class
{   
    private $pt;
    private $url;
    private $path;

    /**
     * Construct 
     *
     * @return void
     **/
    public function __construct( $pt )
    {       
        $this->pt = $pt;
        $this->url = plugins_url( '', __FILE__ );
        $this->path = plugin_dir_path( __FILE__ );
        add_action( 'init', array( $this, 'init_all' ) );
    }


    /**
     * Dispatch general hooks
     *
     * @return void
     **/
    public function init_all() 
    {
        add_action( 'wp_enqueue_scripts',   array( $this, 'frontend_enqueue' ) );
        add_filter( 'body_class',           array( $this, 'add_body_class' ) );
        add_filter( 'template_include',     array( $this, 'custom_template' ) );
    }

    /**
     * Use for custom frontend enqueues of scripts and styles
     * http://codex.wordpress.org/Plugin_API/Action_Reference/wp_enqueue_scripts
     *
     * @return void
     **/
    public function frontend_enqueue() 
    {
        global $post;
        if( $this->pt != get_post_type( $post->ID ) )
            return;         
    }

    /**
     * Add custom class to body tag
     * http://codex.wordpress.org/Function_Reference/body_class
     *
     * @param array $classes
     * @return array
     **/
    public function add_body_class( $classes ) 
    {
        global $post;
        if( $this->pt != get_post_type( $post->ID ) )
            return $classes;

        $classes[] = $this->pt . '-body-class';
        return $classes;
    }

    /**
     * Use the plugin template file to display the CPT
     * http://codex.wordpress.org/Conditional_Tags
     *
     * @param string $template
     * @return string
     **/
    public function custom_template( $template ) 
    {
        $post_types = array( $this->pt );
        $theme = wp_get_theme();

        if ( is_post_type_archive( $post_types ) )
            $template = $this->path . '/single-virtual-cpt.php';

        if ( is_singular( $post_types ) )
            $template = $this->path . '/single-virtual-cpt.php';

        return $template;
    }

}

Template file

single-virtual-cpt.php
put in the same folder as the plugin file

<?php
/**
 * A custom -not from the theme- template
 *
 * @package WordPress
 * @subpackage Virtual_Template
 */

get_header(); 

while ( have_posts() ) : the_post(); $theID = $post->ID; 
    _e('This is a virtual template for the post:<br />');
    the_title();
endwhile; 
get_footer(); 

?>
</body>
</html>

Result

virtual template in frontend
click to enlarge ⤴

share|improve this answer
    
This is something I've mulled over, but as your screenshot at the end shows, you're missing some container HTML that adds padding. Also, the sidebar is missing (if there is one). From your description, I'm getting the feeling what I'm looking for simply isn't possible - would that be correct? –  Andy Adams Dec 11 '12 at 16:35
    
@AndyAdams, as I said, you have to conform the plugin's template to suit your desired output. In my example, I'm just echoing a phrase and printing the title: it's a barebone template... You can do pretty much what you want with it, have you tried the code? –  brasofilo Dec 11 '12 at 17:53
    
Can you post a couple of pastebins? 1) The normal archive.php. 2) A modified version showing your desired output. –  brasofilo Dec 11 '12 at 18:07
    
Well, the issue is I'm trying to account for any theme, so I can't simply fill in a template - let me know if that makes sense. –  Andy Adams Dec 11 '12 at 19:51
    
The any theme detail is not present in the Question. That makes a no, my answer doesn't apply. @kaiser's approach seems the best one so far. –  brasofilo Dec 11 '12 at 19:55
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.