In previous articles, we stepped through making a plugin from the folder to the file and adding some practical functionality.
This article will dive in much deeper and learn how to create a plugin and prepare it to release in the world for production-ready sites.
Fair warning
Unlike some of my other posts, this one will get a bit deeper into the technical weeds by exploring WordPress plugin development writing object-oriented PHP.
The good news is, there's a pretty sweet generator that creates a nice baseline for you. The challenging news is that you need to be familiar with OOP or object-oriented programming to understand what the hell is going on.
It can get a bit overwhelming, but if you stick with it — it'll make sense in the end.
The scope
We're going to keep this scope pretty narrow since there's going to be a lot to cover by creating a plugin that will add a new "Movie" custom post type to the backend of WordPress.
Let's build!
Here's an outline of what we will need to do:
- Generate the plugin boilerplate
- Update the plugin description
- Add our custom post type code
- Register our custom post type using a hook
Generate the plugin boilerplate
There's a nifty and swifty plugin generator called, you guessed it, WordPress plugin boilerplate generator.
Head over to https://wppb.me/ and fill out the following:
- Plugin Name - Movies
- Plugin Slug - movies
- Plugin URL - millertchris.com
- Author Name - Chris Miller
- Author Email - @millertchris
- Author URL - millertchris.com
It would be ideal if there's a dedicated landing page for the plugin, but I'm using my personal domain as a placeholder in the example above.
Once you have all of that filled out, click "Build Plugin," and it'll download a .zip file with the name of your plugin.
Update the plugin description
Go ahead and unzip the .zip file and open the folder in your preferred code editor.
The first thing we are going to do is open the movies.php
file and edit the description to read "Adds a new post type called movies."
When you're done, the top of the file should look something like this.
<?php
/**
* The plugin bootstrap file
*
* This file is read by WordPress to generate the plugin information in the plugin
* admin area. This file also includes all of the dependencies used by the plugin,
* registers the activation and deactivation functions and defines a function
* that starts the plugin.
*
* @link http://millertchris.com
* @since 1.0.0
* @package Movies
*
* @wordpress-plugin
* Plugin Name: Movies
* Plugin URI: http://millertchris.com
* Description: Adds a new post type called movies.
* Version: 1.0.0
* Author: Chris Miller
* Author URI: http://millertchris.com
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: movies
* Domain Path: /languages
*/
Once that's done, we should see it reflected in the backend of WordPress when we navigate to the plugins page.
Add our custom post type code
Next, let's navigate the public
folder and open the class-movies-public.php
file to create our custom post type by repurposing an existing function.
Don't worry; some of these functions are there as placeholders, so it's totally fine that we are refactoring ourselves.
Let's go with the last one called public function enqueue_scripts()
on line 85.
Start by deleting lines 87 through 99 and renaming the function to register_movies()
.
When you're done, it should look something like this:
public function register_movies() {
}
Now we can grab the code from the previous article and paste it into this function.
$labels = array(
'name' => _x('Movies', 'Post Type General Name', 'text_domain'),
'singular_name' => _x('Movie', 'Post Type Singular Name', 'text_domain'),
'menu_name' => __('Movies', 'text_domain'),
'name_admin_bar' => __('Movie', 'text_domain'),
'archives' => __('Item Archives', 'text_domain'),
'attributes' => __('Item Attributes', 'text_domain'),
'parent_item_colon' => __('Parent Item:', 'text_domain'),
'all_items' => __('All Items', 'text_domain'),
'add_new_item' => __('Add New Item', 'text_domain'),
'add_new' => __('Add New', 'text_domain'),
'new_item' => __('New Item', 'text_domain'),
'edit_item' => __('Edit Item', 'text_domain'),
'update_item' => __('Update Item', 'text_domain'),
'view_item' => __('View Item', 'text_domain'),
'view_items' => __('View Items', 'text_domain'),
'search_items' => __('Search Item', 'text_domain'),
'not_found' => __('Not found', 'text_domain'),
'not_found_in_trash' => __('Not found in Trash', 'text_domain'),
'featured_image' => __('Featured Image', 'text_domain'),
'set_featured_image' => __('Set featured image', 'text_domain'),
'remove_featured_image' => __('Remove featured image', 'text_domain'),
'use_featured_image' => __('Use as featured image', 'text_domain'),
'insert_into_item' => __('Insert into item', 'text_domain'),
'uploaded_to_this_item' => __('Uploaded to this item', 'text_domain'),
'items_list' => __('Items list', 'text_domain'),
'items_list_navigation' => __('Items list navigation', 'text_domain'),
'filter_items_list' => __('Filter items list', 'text_domain'),
);
$args = array(
'label' => __('Movie', 'text_domain'),
'description' => __('Movie Description', 'text_domain'),
'labels' => $labels,
'supports' => false,
'taxonomies' => array('category', 'post_tag'),
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 5,
'show_in_admin_bar' => true,
'show_in_nav_menus' => true,
'can_export' => true,
'has_archive' => true,
'exclude_from_search' => false,
'publicly_queryable' => true,
'capability_type' => 'page',
);
register_post_type('movie', $args);
Notice how we left out the add_action hook and the wrapping function from our previous code example in the previous article. That's because the add_action
hooks will be used in a separate file, and our wrapping function is not needed since we are placing all of our code into the newly defined function.
Register our custom post type using a hooked
We are almost done — the last thing we need to do is navigate to the includes folder and edit our class-movies.php
file.
On line 174, you will notice the following code:
$this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_scripts' );
This is referencing the file and the function we were editing, so what we will need to do is update this line to use our function and the proper action hook to the following.
$this->loader->add_action( 'init', $plugin_public, 'register_movies' );
That's it! You can now navigate to the backend of your WordPress site, and as long as your plugin is active — you should see the new "Movies" custom post type.
Wrapping up
So there you have it; in this series, we covered that making a WordPress plugin can be as easy as creating a folder with a file and as complex as using a tried and true boilerplate to build an OOP plugin and ship nice, neat code that's ready for the marketplace.
Now I didn't get into the nitty-gritty about all of the wonderful things this boilerplate does for you — but suffice it to say that if you explore the boilerplate and read their amazing comments, you'll begin to learn and see the value it adds to the developer experience.
We're currently brewing a new Zoom plugin for WordPress that I plan to write about in the near future that will go into greater detail in how to build a real-world plugin.
I want to remind you that a plugin does not need to be complicated, complex, or feature-rich to add value to your client's sites or to any open-source community.
Just remember that the plugin ecosystem helps every one, no matter how big or small your idea might be.
Keep reading
This is a series that will teach you how to build a plugin from scratch.
We will get out hands dirty, and by the end of this series, you should be able to take an idea that you have and turn it into a plugin yourself!
Scroll down to find the other articles in this series.