1

I'm writing a new plugin, and one of the things it does is create a new dynamic block. I generally write my plugins based off of WPPB, which does things in an object-oriented way, and semantically separates admin functionality from public functionality.

I have the admin class successfully creating the edit side of the block. But for the "save" side, for a dynamic block, it makes semantic sense for the render function to be in the public class ... but (I think) it needs to get registered in the admin class (actually, now that I'm writing this, is there any harm to calling register_block_type more than once?).

I'm thinking there's more than one way to skin this cat, and I'm trying to figure out the "best", where "best" is making optimal use of both PHP OOP and WordPress builtin functionality, to maximize speed, minimize size, and provide clean, readable code.

Option 1: make sure the main plugin class instantiates the public class first, and then passes it to the admin class:

class My_Plugin {
        $my_public = new My_Public();
        $my_admin = new My_Admin($my_public);
}

class My_Admin {
        public function __construct($my_public) {
                $this->my_public = $my_public;
        }

        register_block_type( 'my-plugin/my-block',
                    array(
                        'editor_script' => 'my_editor_function',
                        'render_callback' => array($this->my_public, 'my_save_function')
        ) );
}

class My_Public {
        public function my_save_function() {
                //do stuff to render the public side of the block
        }
}

Option 2: do something with WordPress Actions and Hooks:

class My_Admin {
        register_block_type( 'my-plugin/my-block',
                    array(
                        'editor_script' => 'my-editor-function',
                        'render_callback' => 'render_front_end')
        ) );

        public function render_front_end() {
                do_action('my_plugin_render_action');
        }
}

class My_Public {
        public function my_save_function() {
                //do stuff to render the public side of the block
        }
        add_action('my_plugin_render_action', 'my_save_function', 10);
}

Or, hypothetically, Option 3: if there's no problem calling the register function more than once:

class My_Admin {
        register_block_type( 'my-plugin/my-block',
                    array(
                        'editor_script' => 'my-editor-function',
        ) );
}

class My_Public {
        register_block_type( 'my-plugin/my-block',
                    array(
                        'render_callback' => 'my_save_function'
        ) );

        public function my_save_function() {
                //do stuff to render the public side of the block
        }
}

Is one of these preferable? Do all of these work? Is there a better way to do this?

6
  • Option 2 and 3 will not work because that's now how the callable type works.'function_name' refers to a function, not a class function. The TLDR is that render_callback takes a value of type callable, which is a PHP thing not a WordPress thing. See php.net/manual/en/language.types.callable.php – Tom J Nowell Sep 30 '20 at 9:18
  • Otherwise, can you clearly state a single question without ambiguity? You need to be able to mark an answer as factually correct for all people with this question but you've asked multiple questions and it's not clear specifically what you're asking, remember this isn't a discussion forum – Tom J Nowell Sep 30 '20 at 9:20
  • I would also note that it's bad practice to spread things out over several classes like that. Objects are supposed to have responsibilities, what you're describing is not OOP but class based procedural programming – Tom J Nowell Sep 30 '20 at 9:22
  • Thanks for the comments @TomJNowell. Sorry if it came out as more of a discussion in my details, but this is definitely just the question in the question field: "How do you render_callback for register_block_type to a method in another class?" So, aside from the potential bad practice part of things, do I take it that my option 1 is the answer to the question? – philolegein Sep 30 '20 at 11:44
  • Re: 2 and 3 not working because they're not callable, do I just need to change the argument to be an array (e.g., instead of render_callback => render_front_end, do render_callback => array( $this, render_front_end)? – philolegein Sep 30 '20 at 11:53
4

Option 1 is the solution, here's a smaller example:

$obj = new Obj();
....
register_block_type(
    'my-plugin/my-block',
    [
        'editor_script' => 'editor-script-handle',
        'render_callback' => [ $obj, 'block_save_function' ]
    ]
);

In that code [ $obj, 'block_save_function' ] is equivalent to $obj->block_save_function(....

The important thing to note, is that render_callback's value is a PHP callable type. You need a reference to the object, and the name of the method to call. This is true of all callbacks, such as when you add an action or filter.

Likewise, option 2 and 3 can be fixed by using the same syntax.

To learn more about the different ways to create callables, see the PHP official documentation here https://www.php.net/manual/en/language.types.callable.php

1

In PHP you can use an array to call methods (functions) within classes. The first part is the class name and the second the method. It is not necessary to instantiate that class and the class name can be a string as well. Even namespaces are working fine.

So how to define the callback for the method render in the following code?

namespace Any_Vendor\My_Plugin;

class My_Block
{
    public static function render($attributes) { ... }
}
register_block_type(
    'my-plugin/my-block',
    [
        'render_callback' => [ \Any_Vendor\My_Plugin\My_Block::class, 'render' ],
        ...
    ]
);

Using a string [ '\Any_Vendor\My_Plugin\My_Block', 'render' ] would do the job as well, but you should stick to the first example [ \Any_Vendor\My_Plugin\My_Block::class, 'render' ] because the string variant is hard to be interpreted by IDEs and editors.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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