Anfragen

Additional fields in the Classic WordPress editor

There are several fields you can use to create your posts, such as title, excerpt, content, author and taxonomy terms which come with a standard user interface. But sometimes this is not enough and you want to add more information about your post.

This article shows different implementations to add a user interface to the Classic WordPress editor and how to persist your data.

If you are using Gutenberg editor have a look at my article “Additional fields in the Gutenberg WordPress editor”.


So let’s add an input field to define the reading time of a post. The classic way to do that is to use a post meta box.

Post meta boxes will still work with Gutenberg, but they are not the recommended way anymore.

To add a post meta box, we can use the add_meta_boxes action. When this action is triggered, it is the right time to use the add_meta_box function, which requires at least an ID, a title, that will be in the header of the box, and a callable parameter that will be responsible for rendering the meta box content. Every time a post is saved the action post_save is triggered in a POST request which will automatically include your input fields.

Anonymous

Anonymous functions can be a very quick way to implement a post meta box with data persistence.

add_action('add_meta_boxes', function(){
  add_meta_box(
      'reading-time',
      'Reading time',
      function(){
          $value = intval(get_post_meta(get_the_ID(), 'reading_time', true));
          echo "<input type='number' name='reading_time' value='$value' />";
      }
  );
});

add_action('save_post', function($post_id){
  update_post_meta($post_id, 'reading_time', intval($_POST["reading_time"]))
});

Functional

With the functional approach, we can add helper functions that can be used to store and access the data in themes or other plugins. Refactoring becomes much easier because the implementation of data persistence is wrapped in custom functions (get_reading_time, set_reading_time) and hidden from the outside. Editors like VSCode provide code completion to use these functions.

namespace PublicFunctionOrg\WordPress\CodeSamples;

function add_meta_boxes(){
  add_meta_box(
      'reading-time',
      'Reading time',
      __NAMESPACE__."\render_meta_box"
  );
}
add_action('add_meta_boxes', __NAMESPACE__."\add_meta_boxes");

function render_meta_box(){
  $value = get_reading_time(get_the_ID());
  echo "<input type='number' name='reading_time' value='$value' />";
}

function save_post($post_id){
  set_reading_time($post_id, sanitize_text_field($_POST["reading_time"]))
}
add_action('save_post', __NAMESPACE__."\save_post");

function get_reading_time($post_id){
  return intval(get_post_meta($post_id, 'reading_time', true));
}

function set_reading_time($post_id, $reading_time){
  update_post_meta($post_id, 'reading_time', intval($reading_time));
}

Component class

For large projects where you may need more than just one additional meta box, a component-based approach can significantly reduce the lines of code. An IDE like PHPStorm can also help you use the singleton instance properties for easy access to the meta values in themes. Refactoring will be assisted and the implementation of the data persistence is hidden from the outside as well.

// plugin.php
namespace PublicFunctionOrg\WordPress\CodeSamples;

class Plugin {

private function __construct(){
  $this->readingTimeMeta = new PostMetaNumber("reading_time", "Reading time");
}
private static $instance = null;
public static function instance() {
  if ( null == static::$instance ) {
    static::$instance = new static();
  }
  return static::$instance;
}
}

Plugin::instance();
// post-meta-box-number-component.php
namespace PublicFunctionOrg\WordPress\CodeSamples;

class PostMetaNumber {

public function __construct($meta_key, $title){
  $this->meta_key = $meta_key;
  $this->title = $title;

  add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
  add_action( 'save_post', array( $this, 'save_post' ) );
}

public function add_meta_boxes(){
  add_meta_box(
      $this->meta_key,
      $this->title,
      array( $this, 'render' )
  );
}

public function render(){
  $value = $this->get(get_the_ID());
  $key = $this->meta_key;
  echo "<input type='number' name='$key' value='$value' />";
}

public function save_post($post_id){
  $this->set($post_id, sanitize_text_field($_POST[$this->meta_key]));
}

public function set($post_id, $value){
  update_post_meta($post_id, $this->meta_key, intval($value));
}

public function get($post_id){
  return intval(get_post_meta($post_id, $this->meta_key, true));
}

}

My personal favorite is the component-based approach, even though I’m principally a big fan of functional programming. The reason is that the documentation in code comes along with the object property hierarchy. The possibility to use PSR-4 autoloading is a huge help for later code refactoring. But in smaller plugins or in simple themes, the functional approach can also work very well.

In the code examples, I used post meta values to store the reading time. In a real project I would not recommend this. If you want to know why and how to store this kind of data better read the following article: Do not use Post Meta


ADS for Plugin and Theme starterkit