I'm currently working on writing my first OOP Plugin for Wordpress.
To help out a little bit with structure I looked for, and found, a boiler plate that sets up the basics for me.
In Main.php
there's a method that loads JS and CSS assets for admin:
/**
* Register all of the hooks related to the admin area functionality
* of the plugin.
*
* @since 0.1.0
* @access private
*/
private function define_admin_hooks() {
$plugin_admin = new Admin\Controller( $this->get_plugin_name(), $this->get_version(), $this->get_plugin_path() );
$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
}
So far, so good. But as my plugin grows in complexity, it feels like this method is gonna get unwieldy with alot of hooks.
Here's an example with a CPT setup and a setting page added
/**
* Register all of the hooks related to the admin area functionality
* of the plugin.
*
* @since 0.1.0
* @access private
*/
private function define_admin_hooks() {
$plugin_admin = new Admin\Controller( $this->get_plugin_name(), $this->get_version(), $this->get_plugin_path() );
$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
$interactWithVimeo = new Admin\InteractWithVimeo();
$this->loader->add_action ( 'admin_init', $interactWithVimeo, 'setCredentials');
$cpt = new Admin\CustomPostType();
// Create the custom post type
$this->loader->add_action ( 'init', $cpt, 'create_post_type' );
// Remove post row actions
$this->loader->add_filter ( 'post_row_actions', $cpt, 'remove_row_actions', 10, 2 );
$settingsPage = new Admin\SettingsPage();
// Add the settings page to CPT menu
$this->loader->add_action ( 'admin_menu', $settingsPage, 'add_settings_page' );
}
At this point I'm wondering if it would be better to simply setup the different classes to load their own hooks within themselves to avoid the clutter in Main.php
Such as
/**
* Register all of the hooks related to the admin area functionality
* of the plugin.
*
* @since 0.1.0
* @access private
*/
private function define_admin_hooks() {
$myExensiveClassWithAlotOfMethods = new Admin\MyExensiveClassWithAlotOfMethods();
$this->loader->add_action ( 'admin_init', $myExensiveClassWithAlotOfMethods, 'init' );
}
And the init method in the class then contains all the hooks that the class needs.
Is this a bad idea? There seems to be different ways to do it of course. I'm just curious which approach seems to be inline with the boiler plate.
I'm currently working on writing my first OOP Plugin for Wordpress.
To help out a little bit with structure I looked for, and found, a boiler plate that sets up the basics for me.
In Main.php
there's a method that loads JS and CSS assets for admin:
/**
* Register all of the hooks related to the admin area functionality
* of the plugin.
*
* @since 0.1.0
* @access private
*/
private function define_admin_hooks() {
$plugin_admin = new Admin\Controller( $this->get_plugin_name(), $this->get_version(), $this->get_plugin_path() );
$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
}
So far, so good. But as my plugin grows in complexity, it feels like this method is gonna get unwieldy with alot of hooks.
Here's an example with a CPT setup and a setting page added
/**
* Register all of the hooks related to the admin area functionality
* of the plugin.
*
* @since 0.1.0
* @access private
*/
private function define_admin_hooks() {
$plugin_admin = new Admin\Controller( $this->get_plugin_name(), $this->get_version(), $this->get_plugin_path() );
$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
$interactWithVimeo = new Admin\InteractWithVimeo();
$this->loader->add_action ( 'admin_init', $interactWithVimeo, 'setCredentials');
$cpt = new Admin\CustomPostType();
// Create the custom post type
$this->loader->add_action ( 'init', $cpt, 'create_post_type' );
// Remove post row actions
$this->loader->add_filter ( 'post_row_actions', $cpt, 'remove_row_actions', 10, 2 );
$settingsPage = new Admin\SettingsPage();
// Add the settings page to CPT menu
$this->loader->add_action ( 'admin_menu', $settingsPage, 'add_settings_page' );
}
At this point I'm wondering if it would be better to simply setup the different classes to load their own hooks within themselves to avoid the clutter in Main.php
Such as
/**
* Register all of the hooks related to the admin area functionality
* of the plugin.
*
* @since 0.1.0
* @access private
*/
private function define_admin_hooks() {
$myExensiveClassWithAlotOfMethods = new Admin\MyExensiveClassWithAlotOfMethods();
$this->loader->add_action ( 'admin_init', $myExensiveClassWithAlotOfMethods, 'init' );
}
And the init method in the class then contains all the hooks that the class needs.
Is this a bad idea? There seems to be different ways to do it of course. I'm just curious which approach seems to be inline with the boiler plate.
Sally CJ raises a good point in their comment that it is probably better to have each class initialize its own actions and filters.
I've been working with the same boiler plate for some time now and created my own version of this boiler plate on GitHub.
In that version I have a Back\Hooks
and a General\Hooks
, both Singletons and getting passed the Loader
. This is how the General\Hooks
looks in one project
<?php
namespace ACME\General;
class Hooks
{
protected static $instance;
protected $loader;
public static function getInstance($loader)
{
if (self::$instance === null) {
self::$instance = new self($loader);
}
return self::$instance;
}
protected function __construct($loader)
{
$this->loader = $loader;
}
public function run()
{
$this->addActions();
$this->addFilters();
}
protected function addActions()
{
AdvancedCustomFields\Options::add($this->loader);
$this->loader->addAction('init', Shortcode\News::class, 'init');
$this->loader->addAction('init', Shortcode\Courses::class, 'init');
$this->loader->addAction('wp_footer', WooCommerce\Foo::class, 'print_modal');
// and many more
}
protected function addFilters()
{
$this->loader->addFilter('the_content', Bar\Baz::class, 'filter_the_content', 999);
// and many more
}
}
Now in the case of AdvancedCustomFields\Options
I already pass the loader and do the initialization inside that class, doing that for all the other cases would probably be much cleaner.
General\Shortcode\News
looks like this
<?php
namespace ACME\General\Shortcode;
class News
{
public static function init()
{
add_shortcode('acme_news', [__CLASS__, 'render']);
}
public static function render($raw_atts = [], $content = null, $tag = '')
{
$raw_atts = array_change_key_case((array)$raw_atts, CASE_LOWER);
$atts = shortcode_atts([
'size' => 'large',
'amount' => '4',
'offset' => '0',
'exclude' => '',
], $raw_atts, $tag);
/* filter $atts */
$atts['amount'] = intval($atts['amount']);
$atts['offset'] = intval($atts['offset']);
/* get result */
$result = '';
// actual code, doing WP_Query, etc.
// never directly outputted but only saved in $result
return $result;
}
add_action
a one-liner (and should be better for memory). But will switch to a more "dependancy injection" style soon – kero Commented Oct 5, 2018 at 12:43class
should only initialize its own hooks, and if the class instantiates other classes, let the classes initialize their own hooks. There are exceptions, but that depend on your code. – Sally CJ Commented Oct 5, 2018 at 13:19