Alright! Let me be straight forward!
Defining a custom post type or a custom taxonomy inside a theme is cruel and a bad idea.
I created websites for most of the popular brands in my city and except for few, every one of them moved on to a different theme altogether after eight to ten months.
“Why! Did you write bad code?”
Na! The thing is, I got all those jobs from a digital marketing agency and a client’s relationship with a digital marketing agency is not everlasting! Businesses with websites keep switching digital marketing agencies all the time and every time they switch, chances are they’ll switch the look and feel of the website too!
We can not blame anybody in the above chain.
But it is our responsibility to not cause any data loss when the client moves to a different theme.
And here is how the data loss occurs.
Common, go ahead and switch to any default theme or custom from the WordPress market place.
I went ahead and switched to the “twentynineteen” default theme and here is how the admin dashboard looks like now:
Ouch! After switching to a different theme, we can no longer access the Reviews panel or its taxonomy.
This is a very big problem!
I created 12 Reviews and now I can no longer access them nor display them.
Imagine if it is a custom post type for a school’s student record. If the client is maintaining a students record with more than 1000 entries and If the client switches from a theme where this “students” post type is registered, there is no way to access those student records anymore.
Of course, the data is not technically lost. It is still there in the database. Moving the custom post type code to the new theme does the trick of bringing back those student records.
But, what if the client doesn’t know how to do it? What if the new developer is not aware of the “students” post type at all?
The reality is, most clients and developer doesn’t care. They will re-do all the work again.
The bottom line is, let’s not think of a disaster and a hell-full situation like above.
Let’s feel responsible and move our newly created custom post type and custom taxonomy to a plugin and avoid the above situation altogether.
“I am scared! Now I have to create a plugin?”
Don’t be scared, we just need to create a pretty basic plugin which gets the job done and Creating a basic plugin is extremely easy.
Go to:
/wp-content/plugins
and create a new directory and name it “dosth-custom-functionality”:
/wp-content/plugins/dosth-custom-functionality
It is important that we namespace our plugin directory name as well. Tons of plugins in the market brings us a ton of naming errors. So, when you are creating a plugin, always namespace its directory name as uniquely as possible.
dosth-custom-functionality
In our case, we are creating a plugin which houses the custom functionality of the Dosth App. So, I namespaced the plugin directory name with “dosth”.
Next, inside this directory, create a PHP file with the same name as the directory. That is dosth-custom-functionality.php
.
/wp-content/plugins/dosth-functionality-plugin/dosth-custom-functionality.php
It is not mandatory that plugin directory name and main file of the plugin to share the same name. It is just a good convention to follow. This convention helps future developers of the plugin to easily find the file which kick starts the plugin.
Next, put the following comment at the top of the dosth-custom-functionality.php
file.
<?php /*
**************************************************************************
Plugin Name: Dosth Custom Functionality
Description: Contains Custom Post type and taxonomy registrations
Version: 1.0.0
Author: Naresh Devineni
Author URI: https://www.usablewp.com/
Text Domain: nd_dosth_plugin
License: GPL2
License URI: https://www.gnu.org/licenses/gpl-2.0.html
************************************************************************** */
Does it look familiar?
“Yep! We created a comment like this for our theme’s style.css
file, correct?”
One Hundred Percent correct!
The comment at the top of the style.css
file tells WordPress that it is a theme.
Similarly, The comment at the top of the plugin file tells WordPress that it is a plugin.
Here is the updated plugins directory structure:
If we now go the Admin Dashboard -> Plugins, we can see our newly created plugin.
Quick and easy, right?
WordPress is making good use of all the information we kept inside that special comment.
Also, While we are here on the Plugins Screen, just go ahead and activate our newly created functionality plugin. Because the main plugin file is empty with just a special comment, activating it doesn’t cause any effect.
Next, move the custom post type and custom taxonomy registrations to the dosth-custom-functionality.php
file from our theme’s functions.php
file.
Once you are done moving the code, just refresh the Admin Dashboard.
And, Voila!. Everything is working as expected. If we now go to the “Reviews” panel, we can still access all the reviews we have entered previously.
Here is why everything works even if we changed the location of the above code.
We are registering both custom post types and custom taxonomies by hooking into the “init” action hook. So, WordPress is not registering custom post types and taxonomies as soon as it comes across the code inside the functions.php
file. It is still waiting until the “init” action hook is fired.
And this is why we were able to move our custom post type registrations to the plugin.
If you see the flow of WordPress:
WordPress will first load plugins -> then the functions.php
file of the active theme -> then fires “init” action hook.
So, If the code is tied to an action hook which fires after the functions.php
file of the active theme is loaded, then we can place the same code inside a plugin without any doubt because the code is still not going to get executed until the “init” action hook is fired. It is just that WordPress will come across our code earlier, but it doesn’t execute it earlier.
Technically, based on the firing time of a particular action hook, the location of our code doesn’t really matter in WordPress.
This is why there are so many plugins for WordPress.
Get It?
Are you able to see the flexibility of WordPress yet?
And, This is pretty much how you create a basic plugin in WordPress. But this is not the best way though. I might have done something wrong here. For example, I did not load the plugin text domain as we loaded it for our theme.
Coming to the point, You are almost done with the theme development. So, it is a good idea for you to start learning WordPress Plugin Development to understand and use WordPress properly to its best abilities.
Here is the updated dosth-custom-functionality.php
file:
<?php /*
**************************************************************************
Plugin Name: Dosth Custom Functionality
Description: Contains Custom Post type and taxonomy registrations
Version: 1.0.0
Author: Naresh Devineni
Author URI: https://www.usablewp.com/
Text Domain: nd_dosth_plugin
License: GPL2
License URI: https://www.gnu.org/licenses/gpl-2.0.html
************************************************************************** */
/**
* Register Custom Post Types for Dosth Site.
*
* @link https://codex.wordpress.org/Function_Reference/register_post_type
*/
function nd_dosth_register_custom_post_types(){
//Register Reviews Post Type
register_post_type( 'dosth_reviews',
array(
'labels' => array(
'name' => __( 'Reviews', 'nd_dosth' ),
'singular_name' => __( 'Review', 'nd_dosth' ),
'add_new' => __( 'Add Review', 'nd_dosth' ),
'add_new_item' => __( 'Add New Review', 'nd_dosth' ),
'edit_item' => __( 'Edit Review', 'nd_dosth' ),
'all_items' => __( 'All Reviews', 'nd_dosth' ),
'not_found' => __( 'No Reviews Found', 'nd_dosth' ),
),
'menu_icon' => 'dashicons-format-quote',
'public' => true,
'exclude_from_search' => false,
'has_archive' => true,
'hierarchical' => false,
'show_in_rest' => true,
'rewrite' => array( 'slug' => 'reviews' ),
'supports' => array( 'title', 'editor', 'custom-fields', 'thumbnail', 'excerpt', 'revisions', 'page-attributes' ),
//'taxonomies' => array( 'category', 'post_tag' )
)
);
}
add_action('init', 'nd_dosth_register_custom_post_types');
function nd_dosth_register_custom_taxonomies(){
// Add new taxonomy, make it hierarchical (like categories)
$labels = array(
'name' => _x( 'Review Sources', 'taxonomy general name', 'nd_dosth' ),
'singular_name' => _x( 'Review Source', 'taxonomy singular name', 'nd_dosth' ),
'search_items' => __( 'Search Review Sources', 'nd_dosth' ),
'all_items' => __( 'All Review Sources', 'nd_dosth' ),
'edit_item' => __( 'Edit Review Source', 'nd_dosth' ),
'update_item' => __( 'Update Review Source', 'nd_dosth' ),
'add_new_item' => __( 'Add New Source', 'nd_dosth' ),
'not_found' => __( 'No Review Sources Found!', 'nd_dosth' ),
);
$args = array(
'hierarchical' => true, // Like Category Taxonomy. False is like Tag taxonomy.
'labels' => $labels,
'show_ui' => true,
'show_admin_column' => true,
'show_in_rest' => true,
'has_archive' => true,
'rewrite' => array('slug' => 'review-source')
);
register_taxonomy( 'dosth_review_source', array( 'dosth_reviews' ), $args );
}
add_action('init', 'nd_dosth_register_custom_taxonomies');
And here is the updated functions.php
file:
<?php
function nd_dosth_enqueue_styles() {
wp_enqueue_style(
'normalize',
get_stylesheet_directory_uri() . '/assets/css/normalize.css',
array(),
false,
'all'
);
wp_enqueue_style(
'bootstrap',
get_stylesheet_directory_uri() . '/assets/css/bootstrap.min.css',
array(),
false,
'all'
);
wp_enqueue_style(
'superfish',
get_stylesheet_directory_uri() . '/assets/css/superfish.css',
array(),
false,
'all'
);
wp_enqueue_style(
'slick',
get_stylesheet_directory_uri() . '/assets/css/slick.css',
array(),
false,
'all'
);
wp_enqueue_style(
'ubuntu-font',
'https://fonts.googleapis.com/css?family=Ubuntu:300,400,400i,700',
array(),
false
);
wp_enqueue_style(
'main-stylesheet',
get_stylesheet_uri(),
array('normalize', 'bootstrap'),
"8.0",
'all'
);
}
add_action( 'wp_enqueue_scripts', 'nd_dosth_enqueue_styles' );
function nd_dosth_enqueue_scripts() {
wp_enqueue_script(
'modernizr',
get_stylesheet_directory_uri() . '/assets/js/modernizr.min.js',
array(),
'1.0.0',
true
);
wp_enqueue_script(
'superfish',
get_stylesheet_directory_uri() . '/assets/js/superfish.min.js',
array('jquery'),
'1.0.0',
true
);
wp_enqueue_script(
'fitvids',
get_stylesheet_directory_uri() . '/assets/js/jquery.fitvids.js',
array('jquery'),
'1.0.0',
true
);
wp_enqueue_script(
'slick',
get_stylesheet_directory_uri() . '/assets/js/slick.min.js',
array('jquery'),
'1.0.0',
true
);
wp_enqueue_script(
'main-js',
get_stylesheet_directory_uri() . '/assets/js/main.js',
array('jquery'),
'1.0.0',
true
);
$translation_array = array(
"email_placeholder" => esc_attr__( 'Enter your email address here', 'nd_dosth' ),
'ajax_url' => admin_url('admin-ajax.php'),
);
wp_localize_script( 'main-js', 'translated_text_object', $translation_array );
}
add_action( 'wp_enqueue_scripts', 'nd_dosth_enqueue_scripts' );
/*-----------------------------------------------------------------------------------*/
/* Adds new body classes
/*-----------------------------------------------------------------------------------*/
add_filter('body_class', 'add_browser_classes');
function add_browser_classes( $classes ){
// WordPress global variables with browser information
global $is_gecko, $is_IE, $is_opera, $is_safari, $is_chrome;
if( $is_chrome ) {
$classes[] = 'chrome';
}
elseif( $is_gecko ){
$classes[] = 'gecko';
}
elseif( $is_opera ) {
$classes[] = 'opera';
}
elseif( $is_safari ) {
$classes[] = 'safari';
}
elseif( $is_IE ) {
$classes[] = 'internet-explorer';
}
return $classes;
}
function nd_dosth_theme_setup() {
/*
* Make theme available for translation.
* Translations can be filed in the /languages/ directory.
*/
load_theme_textdomain( 'nd_dosth', get_stylesheet_directory() . '/languages' );
// Add <title> tag support
add_theme_support( 'title-tag' );
// Add custom-logo support
add_theme_support( 'custom-logo' );
// Add widgets support
add_theme_support( 'widgets' );
// Add Featured Image support
add_theme_support( 'post-thumbnails' );
// Add HTML5 support
add_theme_support( 'html5', array( 'comment-list', 'comment-form', 'search-form', 'gallery', 'caption' ) );
// Add image sizes
add_image_size( 'dosth-blog-thumbnail', 260, 175, true );
// Register Navigation Menus
register_nav_menus( array(
'header' => esc_html__( 'Display this menu in Header', 'nd_dosth' ),
'footer' => esc_html__( 'Display this menu in Footer', 'nd_dosth')
) );
}
add_action( 'after_setup_theme', 'nd_dosth_theme_setup');
/**
* Register widget area.
*
* @link http://codex.wordpress.org/Function_Reference/register_sidebar
*/
function nd_dosth_register_sidebars() {
register_sidebar( array(
'name' => esc_html__( 'Footer Section One', 'nd_dosth' ),
'id' => 'footer-section-one',
'description' => esc_html__( 'Widgets added here would appear inside the first section of the footer', 'nd_dosth' ),
'before_widget' => '<aside id="%1$s" class="widget %2$s">',
'after_widget' => '</aside>',
'before_title' => '<h4 class="widget-title">',
'after_title' => '</h4>',
) );
register_sidebar( array(
'name' => esc_html__( 'Footer Section Two', 'nd_dosth' ),
'id' => 'footer-section-two',
'description' => esc_html__( 'Widgets added here would appear inside the second section of the footer', 'nd_dosth' ),
'before_widget' => '<aside id="%1$s" class="widget %2$s">',
'after_widget' => '</aside>',
'before_title' => '<h4 class="widget-title">',
'after_title' => '</h4>',
) );
register_sidebar( array(
'name' => esc_html__( 'Blog', 'nd_dosth' ),
'id' => 'blog',
'description' => esc_html__( 'Widgets added here would appear inside the all the blog pages', 'nd_dosth' ),
'before_widget' => '<aside id="%1$s" class="widget %2$s">',
'after_widget' => '</aside>',
'before_title' => '<h4 class="widget-title">',
'after_title' => '</h4>',
) );
}
add_action( 'widgets_init', 'nd_dosth_register_sidebars' );
/*
* Custom Excerpt Length
*/
function nd_dosth_custom_excerpt_length() {
return 20;
}
add_filter( 'excerpt_length', 'nd_dosth_custom_excerpt_length' );
/*
* Remove brackets at the end of each excerpt
*/
function nd_dosth_custom_excerpt_more() {
return '...';
}
add_filter( 'excerpt_more', 'nd_dosth_custom_excerpt_more' );
/*
* Outputs the post's thumbnail and title when ID of the post is provided
*/
function nd_dosth_output_post_thumb_and_title( $post_id ){ ?>
<div class="post-info">
<?php // Output Post's Thumbnail ?>
<?php $page_thumb = wp_get_attachment_image_src( get_post_thumbnail_id( $post_id ), 'thumbnail' ); ?>
<?php if( ! empty( $page_thumb[0] ) ) : ?>
<a href="<?php echo get_the_permalink( $post_id ); ?>" class="post-thumb">
<img src="<?php echo $page_thumb[0]; ?>" />
</a>
<?php endif; ?>
<?php // Output Previous page Title ?>
<a class="post-title" href="<?php echo get_the_permalink( $post_id ); ?>">
<?php echo get_the_title( $post_id ); ?>
</a>
</div>
<?php }
/**
* Remove default words from archive titles like "Category:", "Tag:", "Archives:"
*/
function nd_dosth_remove_default_archive_words($title) {
if ( is_category() ) {
$title = single_cat_title( '', false );
} elseif ( is_tag() ) {
$title = single_tag_title( '', false );
} elseif ( is_author() ) {
$title = '<span class="vcard">' . get_the_author() . '</span>' ;
}
return $title;
}
And, that’s it. In the next module, we will deal with the search functionality of WordPress.