Advertisement

Object-Oriented Programming in WordPress: Building the Plugin II

by

In the previous article in this series, we finally began preparing the foundation for the plugin that we're going to be writing.

Specifically, we took a look at the file organization, the components, and the actual details of what the plugin is going to do. We also stubbed out the code that we'll be filling out in this tutorial.

In addition to making our plugin actually do something, we're going to be talking about a number of different object-oriented principles, techniques, and ideas as we work through the plugin. 

Note that in this tutorial we're going to be doing very little documentation. We've covered the details about this in the previous article; however, we'll be talking more about it in the article following this one.

As with the rest of the articles in this series, please be sure to catch up on everything that we've covered thus far in the series as everything that we're doing builds on the previous topics.

For reference, we've covered:

  1. An Introduction
  2. Classes
  3. Types
  4. Control Structures: Conditional Statements
  5. Control Structures: Loops
  6. Functions and Attributes
  7. Scope
  8. Building the Plugin I

With that said, let's pick up where we left off.

Where Do We Start?

When it comes to writing software - regardless of the paradigm that's being used - it's not done so in a linear fashion. That is, we don't necessarily being writing at the starting point of the program. Often times - though not always - that might be one of the latter parts that we right.

With that said, we're going to begin working on each file that makes up the plugin in a way that makes sense as we work through the plugin. By that, I mean that as we work through this article, things may seem scattered at first but should hopefully become a bit clearer as we look at each file.

The Loader

The first class that we're going to complete is located in includes/class-single-post-meta-manager-loader.php. If you recall from the previous article, this class is responsible for coordinating actions and filters between the core plugin and the administration class.

In a sense, it provides a wrapper around WordPress' native hook APIs; however, it allows us to de-couple (and thus enforce a separation of concerns) our classes so that each can specialize on a specific purpose.

First, let's take a look at the class:

<?php

class Single_Post_Meta_Manager_Loader {

    protected $actions;

    protected $filters;

    public function __construct() {

        $this->actions = array();
        $this->filters = array();
    
    }

    public function add_action( $hook, $component, $callback ) {
	    $this->actions = $this->add( $this->actions, $hook, $component, $callback );
    }

    public function add_filter( $hook, $component, $callback ) {
	    $this->filters = $this->add( $this->filters, $hook, $component, $callback );
    }

    private function add( $hooks, $hook, $component, $callback ) {

	    $hooks[] = array(
		    'hook'      => $hook,
		    'component' => $component,
		    'callback'  => $callback
	    );

	    return $hooks;

    }

    public function run() {

        foreach ( $this->filters as $hook ) {
		    add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ) );
	    }

	    foreach ( $this->actions as $hook ) {
		    add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ) );
	    }

    }

}

At this point in the series, you should notice several key things about the class based on the discussions that we've had thus far in the series.

  • There are two protected attributions each of which refer to arrays as defined in the constructor. One is designated for actions, the other for filters.
  • There are two public functions. One is designed to easily add actions, the other is designed to easily add filters. Note that each accepts three components: the hook name, the main object that has the function to be called, and the function to be called during the actual execution of the hook. For more information about actions and filters, see this reference.
  • Next, we have a private function that is used to simplify the previous two public functions such that we have a single place to add the hook to the proper array.
  • Finally, we have a run function is that used to wire up all of the defined hooks. This is what will register all of our custom functions with WordPress.

As we continue to build the rest of the plugin, we'll see this particular class in use.

The Administration Dashboard

This part of the plugin contains all of the files that are located in the admin directory. If you remember from the previous article, we have a primary class, a stylesheet, and single file used to render the view of the content.

We'll look at each of these files in order that they are used starting with the core admin class.

Single Post Meta Manager Admin

This is the core class responsible for registering the stylesheets, the meta box, and including the file that will render the content of the meta box.

Let's take a look at the full code and then we'll review what it's doing.

<?php

class Single_Post_Meta_Manager_Admin {

    private $version;

    public function __construct( $version ) {
        $this->version = $version;
    }

    public function enqueue_styles() {

        wp_enqueue_style(
            'single-post-meta-manager-admin',
            plugin_dir_url( __FILE__ ) . 'css/single-post-meta-manager-admin.css',
            array(),
            $this->version,
            FALSE
        );

    }

    public function add_meta_box() {

        add_meta_box(
            'single-post-meta-manager-admin',
            'Single Post Meta Manager',
            array( $this, 'render_meta_box' ),
            'post',
            'normal',
            'core'
        );

    }

    public function render_meta_box() {
        require_once plugin_dir_path( __FILE__ ) . 'partials/single-post-meta-manager.php';
    }

}

This is a relatively simple class that assumes that you're familiar with wp_enqueue_style and add_meta_box. If not, review the linked articles, and then return to this post.

Next, let's take a look at what the rest of the class is doing:

  • Note that there's a private attribute that is used to track the version of the plugin. This value is passed into the class's constructor and is primarily used in order to make sure that we're including the most recent version of the plugin when enqueueing our stylesheets in order to make sure we're busting any files that may be cached when running this plugin.
  • Next, we have a public function that's used to register the stylesheet associated with the dashboard, and we have a public function that's used to add a meta box to the post type dashboard.
  • Finally, we have another public function (that's technically called from within this class) to render the content of the meta box. The contents of this file are located in an external file that we'll take a look at momentarily.

Though we'll see everything play out in more detail later, you may start to notice that the function that enqueues the stylesheets isn't referenced anywhere else. This is where the Loader class will eventually come into play.

Single Post Meta Manager Partial

Some developers like to write the markup for meta box views within PHP and store them into really long strings. 

I'm not a fan of that approach because views (or partials or templates, or whatever you'd like to call them) and typically used to display data and thus consist of more markup than anything else. To that end, I think that they should be their own file.

In this case, we want to have a file that renders all of the meta data associated with the current post in a table element that's contained within the meta box.

The markup for this file looks like this:

<div id="single-post-meta-manager">

    <?php $post_meta = get_post_meta( get_the_ID() ); ?>
    <table id="single-post-meta-manager-data">
    <?php foreach ( $post_meta as $post_meta_key => $post_meta_value ) { ?>
        <tr>
            <td class="key"><?php echo $post_meta_key; ?></td>
            <td class="value"><?php print_r( $post_meta_value[0] ); ?></td>
        </tr>
    <?php } ?>
    </table>

</div><!-- #single-post-meta-manager -->

Though the markup and the minimum PHP that's contained in this file should be relatively self-explanatory, it does depend on your knowledge of the get_post_meta and get_the_ID functions.

Once all of the post's meta data is retrieved, we then loop through the information (using one of the loop constructs that we covered much earlier) and then display both the meta key and the value.

The Simple Post Meta Admin Styles

The last thing that we need to do for the content in the meta box is to provide the styles in the stylesheet that we've enqueued in the core admin class.

To do that, we'll edit css/simple-post-meta-manager.css.

#single-post-meta-manager-data {
    width:  100%;
}

#single-post-meta-manager-data .key {
    font-weight: bold;
}

Obviously, this is very simple. It doesn't provide anything fancy other than setting the width of the table to 100% of its container, and it bolds the meta key values.

But that's enough for what we're looking to do now.

The Core Plugin File

At this point, we need to define the core plugin file. This is the file that defines the plugin's version, the plugin's slug (which is normally used in internationalization as well as other features), instantiates the Loader, and that registers all of the necessary hooks with WordPress.

Let's take a look at the code, then review it once we've got everything defined:

<?php

class Single_Post_Meta_Manager {

    protected $loader;

    protected $plugin_slug;

    protected $version;

    public function __construct() {

        $this->plugin_slug = 'single-post-meta-manager-slug';
        $this->version = '0.2.0';

        $this->load_dependencies();
        $this->define_admin_hooks();

    }

    private function load_dependencies() {

        require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-single-post-meta-manager-admin.php';

        require_once plugin_dir_path( __FILE__ ) . 'class-single-post-meta-manager-loader.php';
        $this->loader = new Single_Post_Meta_Manager_Loader();

    }

    private function define_admin_hooks() {

        $admin = new Single_Post_Meta_Manager_Admin( $this->get_version() );
        $this->loader->add_action( 'admin_enqueue_scripts', $admin, 'enqueue_styles' );
        $this->loader->add_action( 'add_meta_boxes', $admin, 'add_meta_box' );

    }

    public function run() {
        $this->loader->run();
    }

    public function get_version() {
        return $this->version;
    }

}

The class contains the following attributes:

  • The version which is passed around throughout the plugin in order to help not only to define the current working version, but also to provide functionality such as cache-busting functionality for our stylesheets.
  • There's a plugin slug which can be used for internationalization purposes, as well as other times when a unique identifier is needed.
  • A reference to the loader which we've defined earlier in this file. 

The above attributes are all set in the constructor, but there are also calls to several other functions.

  • load_dependencies is used in order to import all of the files that are used throughout this plugin such as the Admin Manager and the Loader.
  • define_admin_hooks is how we take advantage of the Loader in order to coordinate the functions defined in our Admin class that enqueue our styles and our meta box with WordPress. This is how we're separating the concerns of our plugin and making sure that each class as a single purpose.
  • run is the function that sets everything into motion so that all of the plugin's functionality is running when activated within WordPress.

Except we're still missing a final piece: how do we actually instantiate the core plugin class and kick off the process?

The Plugin Boot Loader

To do this, we take advantage of a file located in the root of the plugin directory. Some people call this a plugin bootstrap file, some call it a boot loader, and some call it the main plugin file.

Whatever you opt to call it, this is the file that registers itself with WordPress and that sets everything into motion. Let's take a look at the code and then we'll review what it does afterward:

<?php
/*
 * Plugin Name:       Single Post Meta Manager
 * Plugin URI:        http://github.com/tommcfarlin/post-meta-manager
 * Description:       Single Post Meta Manager displays the post meta data associated with a given post.
 * Version:           0.2.0
 * Author:            Tom McFarlin
 * Author URI:        http://tommcfarlin.com
 * Text Domain:       single-post-meta-manager-locale
 * License:           GPL-2.0+
 * License URI:       http://www.gnu.org/licenses/gpl-2.0.txt
 * Domain Path:       /languages
 */

if ( ! defined( 'WPINC' ) ) {
    die;
}

require_once plugin_dir_path( __FILE__ ) . 'includes/class-single-post-meta-manager.php';

function run_single_post_meta_manager() {

    $spmm = new Single_Post_Meta_Manager();
    $spmm->run();

}

run_single_post_meta_manager();

The code comment at the top of the file is responsible for telling WordPress that the plugin exists and giving it enough information about the plugin so that it can display it within the dashboard.

The first conditional that you see prevents the plugin file from being accessed directly. This is nothing more than a simply security measure.

Finally, we make a call to require_once to include the core plugin file that we looked at above, and then we define a function and instantiates the Single_Post_Meta_Manager and after which we call run which is what sets everything in motion.

Finally, we make a call to the function that we defined at the very end of the file. This kicks off the process and brings the plugin to life.

What's Up Next?

At this point, we've completed the functionality of our plugin; however, we're still not done. There is still one more thing we need to do in order to make sure that we're following all of the best practices that go into a plugin and that's providing documentation.

In the next post, we'll take a break from the longer form articles of writing code, review the WordPress Documentation Standards, and then we'll document the plugin so that we fully round out all of it's functionality.

In the meantime, download the example plugin, explore how everything fits together, and be sure to leave any comments or questions that you have about our work so far.

Advertisement