OOP development and hooks

admin2025-01-08  5

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.

Share Improve this question asked Oct 5, 2018 at 12:24 INTINT 1,2813 gold badges21 silver badges50 bronze badges 8
  • I flagged this question as off topic and think you'll better be fit to post it on CodeReview. As to what is "inline with the boiler plate" - no idea, you could ask via an issue. I use a similar boilerplate, but with static classes for the actions - makes every 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:43
  • 2 IMHO, a class 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
  • @SallyCJ Would you be able to supply a condensed example of what you mean? – INT Commented Oct 5, 2018 at 13:20
  • Something like this simplified example. But of course that doesn't use the boilerplate in question. Hope it helps.. – Sally CJ Commented Oct 5, 2018 at 14:39
  • 1 Here's a brilliant old thread, wordpress.stackexchange.com/questions/70055/…, and answer, wordpress.stackexchange.com/a/166532/144392, regarding initializing classes. I don't know if this helps you with the boiler plate, but at least I learned a lot when reading that thread and answers to it. – Antti Koskinen Commented Oct 21, 2018 at 18:07
 |  Show 3 more comments

1 Answer 1

Reset to default 0

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;
}
转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1736266244a1104.html

最新回复(0)