red5
red5

Reputation: 312

Dynamic Block: How to include_once a PHP file in the markup?

[Update I added more details after @S.Walsh answered because I am still stuck on how to make an include_once render in the content area.]

My custom plugin includes a Gutenberg dynamic block that uses a PHP function as its 'render_callback'. I would like this render function to do something similar to the PHP include_once() to include a template part.

I can use get_template_part, but how do I tell Wordpress to only do that once despite how many times the block is used (similar to the PHP include_once)?

My block.php looks like this:

register_block_type( __DIR__ . '/build/chart', [
    'render_callback' => 'chart_render',
    'attributes' => [
        'theFile' => [
            'type' => 'string',
            'default' => ''
        ],
        'theVars' => [
            'type' => 'string',
            'default' => ''
        ]
    ]
] );

The output part of render function looks like this:

function chart_render($attr, $content) {
    ob_start();  
    get_template_part($path_to_template_part, null);  
    $ret = ob_get_contents();  
    ob_end_clean();  
    return $ret;    
}

I want to include another separate PHP file that powers the chart, but I only want to include it once, regardless of how many times my block is used in a page/post. This is where I get stuck.

I can simply add include_once() to the render function, but the contents of the file render before the post content loads. In other words, the file I try to include gets output above the frontend HTML tag. I would like it to render with the post content where the block lives.

I tried using the has_block() function as described here by Rich Tabor and I tried the init hook. This approach works for enqueuing scripts, but I get the same problem when trying to use PHP include_once(): The content renders above the tag. I tried this:

function my_dependencies () {
    if(has_blocks('chart')) {
        include_once ('my_path/myfile.php' );
    }
}
add_action( 'init', 'my_dependencies' );

If I put that function in block.php, it will always return false. I suspect that the content blocks do not exist at the time the blocks are registered.

If I put that function in my template part that the render function calls, then it will render the content before the tag.

Not relevant, but you might be asking why I am doing this nonsense? I have a specialized theme that allows my content editors to select certain charts to load in the post, along with an array of parameters the user supplies. Those charts are dependent on a separate PHP file that only needs to appear on the post one time (not one time per block).

_________________________

The Solution based on @S.Walsh's answer @S.Walsh pointed out that it is ok to add include_once() inside of the Output Buffer. That seems so obvious now. Here is what works for me:

function chart_render($attr, $content) {
    ob_start();
    include_once(my_path/myfile.php) // only gets loaded regardless of how many blocks on are the post.
    get_template_part($path_to_template_part, null); // gets loaded for each block on the post. 
    $ret = ob_get_contents();  
    ob_end_clean();  
    return $ret;    
}

Upvotes: 1

Views: 598

Answers (1)

S.Walsh
S.Walsh

Reputation: 3699

Output buffering can be used include your other PHP template file inside the render_callback of your dynamic block.

Updated example based on updated question
Your dynamic block with two attributes, "theFile" and "theVars" have a safe default of '' which is good practise. The PHP template contains a function that returns the chart content based on the saved blocks attributes. When called in the render_callback, with output buffering the contents of the include_once() file is not printed before the block:

template.php

<?php
function render_chart($file, $vars)
{
    // return chart content or other dynamic content
    return "<p>File: ".$file."Vars: ".$vars."</p>";
}

plugin.php

<?php
function chart_render($attrs, $content)
{
    include_once 'src/template.php';
    
    // Start the output buffer
    ob_start();

    // Print the block wrapper attributes (classnames etc)
    echo "<div " . get_block_wrapper_attributes() . ">";

    // Include once the "template.php" file when the block rendered with
    // $block_attributes passed to render_chart() function of the template
    echo render_chart($attrs['theFile'], $attrs['theVars']);

    // Print closing block tag
    echo "</div>";

    // Get the output buffer content to return
    $content = ob_get_clean();

    // End the output buffer
    ob_flush();

    // Returns the content ready to render
    return $content;
}

NB. Keep in mind the differences between require versus include in PHP. Using require() will throw a fatal error and stop the script if there is an issue where include() will only produce a warning - important to note when testing your dynamic code/block.

Upvotes: 1

Related Questions