Calling WordPress get_template_part from inside a shortcode function renders template first

I have a page where I need to allow the user to enter a paragraph of text. Then after that text, insert a shortcode that will render a list of posts, then add more free-form text after that. My thought was that they should be able to insert a shortcode which will output the posts. This way they can simply add the shortcode where they wish the posts to appear.

I currently have the logic which retrieve the posts separated in its own file. Currently I include it in a page by simply using the get_template_part() function:

Read More
get_template_part('donation', 'posts');

I looked into how to create a shortcode and included the following code into my functions.php file in order to create the shortcode:

add_shortcode('donation-posts', 'fnDonatePosts');   
function fnDonatePosts($attr, $content)
{
    get_template_part('donation', 'posts');    
}

The donation-posts.php is being properly executed and the posts are appearing, however, they are always positioned BEFORE the content and not in the location that the shortcode was placed.

I have tried removing the get_template_part() function and just output some text and that works fine. So I understand that the get_template_part() may not be the right way to do this, however, I have not been able to uncover a way to do what I am attempting to do (I am sure that there is a way… I just haven’t found it).

I have tried:

include(get_template_directory(). '/donation-posts.php');
include_once(get_template_directory(). '/donation-posts.php') : 

But these stopped processing once they hit the PHP code in the included file.

I have also tried:

$file = file_get_contents(get_template_directory(). '/donation-posts.php');  
        return $file;

But this only returns the contents of the file (as the function name indicates) which means it doesn’t execute the PHP script to return the posts.

Anybody out there done this before?

Related posts

Leave a Reply

4 comments

  1. You may try this, it may solve your problem because get_template_part basically reacts like PHP's require, it doesn’t return but echos the content immediately where it’s been called.

    add_shortcode('donation-posts', 'fnDonatePosts');   
    function fnDonatePosts($attr, $content)
    {        
        ob_start();  
        get_template_part('donation', 'posts');  
        $ret = ob_get_contents();  
        ob_end_clean();  
        return $ret;    
    }
    
  2. Here is a more dynamic version where you can pass the path to the template.

    function template_part( $atts, $content = null ){
       $tp_atts = shortcode_atts(array( 
          'path' =>  null,
       ), $atts);         
       ob_start();  
       get_template_part($tp_atts['path']);  
       $ret = ob_get_contents();  
       ob_end_clean();  
       return $ret;    
    }
    add_shortcode('template_part', 'template_part');  
    

    And the shortcode:

    [template_part path="includes/social-sharing"]
    
  3. Minimal version of the accepted answer:

    function my_template_part_shortcode() {
        ob_start();
        get_template_part( 'my_template' );
        return ob_get_clean();
    }
    add_shortcode( 'my_template_part', 'my_template_part_shortcode' );
    

    where my-template.php is the file you’d like to include.

  4. get_template_part() didn’t work for me when using it in functions.php. I used locate_template() inside the ob_start and clean instead. For example:

    function full_petition_shortcode( $attr ) {
        ob_start();
        locate_template( 'petition.php', TRUE, TRUE );
    
        return ob_get_clean();
    }
    add_shortcode( 'full-petition', 'full_petition_shortcode' );