Viewed: 1651
Code

Wordpress custom fields

I have only recently looked into using the custom meta boxes that come with Wordpress. I’ve been a fan of Advanced Custom Fields for a long time, so it’s something I’ve never had a great urge to use.

I’m going share with you over the course of this post how to get them set up. You’ll learn to add them efficiently, how to add custom CSS, change placement to under the title and finally how to use them in your theme.

Introducing the code we’ll be using

All meta boxes are built with add_meta_box, which takes 6 parameters. The approach we will be using will build upon this to add them in a more efficient and customisable manner.

add_meta_box( $id, $title, $callback, $screen, $context, $priority, $callback_args );

I have used much of the code, what’s set by the references of the Wordpress Theme Boilerplate and Wordpress Codex.

The code

Find and replace ‘iamsteve’ with your own prefix.

// Start with an underscore to hide fields from custom fields list
$prefix = '_iamsteve_meta_';

$meta_boxes = array(
  array(
    'id' => 'meta-testimonial',
    'title' => 'Role',
    'pages' => array('jetpack-testimonial'), // multiple post types
    'context' => 'advanced', // 'normal', 'advanced', or 'side'
    'priority' => 'high', // 'high', 'core', 'default' or 'low'
    'fields' => array(
      array(
        'name' => 'Role',
        'desc' => 'This can be a company name or role they fulfilled.',
        'id' => $prefix . 'role',
        'type' => 'text',
        'std' => '',
        'placeholder' => 'Photographer'
    ))
  )
);

foreach ($meta_boxes as $meta_box)
{
  $box = new Iamsteve_Meta_Box($meta_box);
}

class Iamsteve_Meta_Box
{
  protected $_meta_box;

  // Create meta box based on given data
  function __construct( $meta_box ) {
    $this->_meta_box = $meta_box;
    add_action( 'admin_menu', array(&$this, 'add') );
    add_action( 'save_post', array(&$this, 'save') );

    // Add our styles to the places they’re required
    add_action( 'admin_print_styles-post-new.php', array(&$this, 'style'), 11 );
    add_action( 'admin_print_styles-post.php', array(&$this, 'style'), 11 );
  }

  function style()
  { 
    wp_enqueue_style('iamsteve-meta-box-style', get_template_directory_uri() . 'css/meta-box.css');
  }

  /// Add meta box for multiple post types
  function add()
  {
    foreach ( $this->_meta_box['pages'] as $page )
    {
      add_meta_box($this->_meta_box['id'], $this->_meta_box['title'], array(&$this, 'show'), $page, $this->_meta_box['context'], $this->_meta_box['priority']);
    }
  }

  // Callback function to show fields in meta box
  function show()
  {
    global $post;

    // Use nonce for verification
    echo '<input type="hidden" name="iamsteve_meta_box_nonce" value="', wp_create_nonce(basename(__FILE__)), '">';

    // Wordpress uses a table based setup for form fields, so following that pattern we will stick to it
    echo '<table class="form-table">';

    foreach ($this->_meta_box['fields'] as $field) {
      // Get current post meta data
      $meta = get_post_meta($post->ID, $field['id'], true);
      echo '<tr>';
      echo '<th class="screen-reader-text"><label for="' . $field['id'] . '">' . $field['name'] . '</label></th>';

      echo '<td>';
        switch ($field['type'])
        {
          case 'text' :
            echo '<input type="text" name="' . $field['id'] . '" id="' . $field['id'] . '" value="' . ( $meta ? $meta : $field['std'] ) . '" size="30">';
            echo '<p><small>' . $field['desc'] . '</small></p>';
            break;

          case 'textarea':
            echo '<textarea name="' . $field['id'] . '" id="' . $field['id'] . '">' . ( $meta ? $meta : $field['std'] ) . '</textarea>';
            echo '<p class="meta-box-description"><small>' . $field['desc'] . '</small></p>';

            break;

          case 'select':
            echo '<select name="' . $field['id'] . '" id="' . $field['id'] . '">';
            foreach ($field['options'] as $option)
            {
              echo '<option value="' . $option['value'] . '"' . ( $meta == $option['value'] ? ' selected="selected"' : '' ) . '>' . $option['name'] . '</option>';
            }
            echo '</select>';
            break;

          case 'radio':
            foreach ($field['options'] as $option)
            {
              echo '<input type="radio" name="' . $field['id'] . '" value="' . $option['value'] . '"' . ( $meta == $option['value'] ? ' checked="checked"' : '' ) . '>' . $option['name'];
            }
            break;

          case 'checkbox':
            echo '<input type="checkbox" name="' . $field['id'] . '" id="' . $field['id'] . '"' . ( $meta ? ' checked="checked"' : '' ) . '>';
            break;
        }
      echo '<td>';

      echo '</tr>';
    }
    echo '</table>';
  }

  // Save data from meta box
  function save($post_id)
  {
    // Verify nonce
    if ( !isset($_POST['iamsteve_meta_box_nonce']) || !wp_verify_nonce($_POST['iamsteve_meta_box_nonce'], basename(__FILE__)) )
    {
      return $post_id;
    }

    // Check autosave
    if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
    {
      return $post_id;
    }

    // Check permissions
    if ( 'page' == $_POST['post_type'] )
    {
      if ( !current_user_can('edit_page', $post_id) )
      {
        return $post_id;
      }
    }
    else if ( !current_user_can('edit_post', $post_id) )
    {
      return $post_id;
    }

    foreach ( $this->_meta_box['fields'] as $field )
    {
      $old = get_post_meta($post_id, $field['id'], true);
      $new = $_POST[$field['id']];

      if ( $new && $new != $old )
      {
        update_post_meta($post_id, $field['id'], $new);
      }
      elseif ( '' == $new && $old )
      {
        delete_post_meta($post_id, $field['id'], $old);
      }
    }
  }
}

Explanation

Here is an overview of what all the code is doing. Generally if you find yourself getting stuck with the code this should help you.

$prefix = ‘iamsteve

You use a prefix with an underscore at the start as this ensures our fields can’t be used anywhere or conflict potentially. You may have seen this in some themes, where you can select custom fields. It can be really confusing for a user if there is no output from it.

$meta_boxes array

The array has a nested array for each meta box you create, which contains the parameters which match up with add_meta_box().

The important part is the ‘fields’ array. Which relies on the function within the class called show. This has a case statement which checks for which field type is passed. You can adjust this here should you need more field types.

The class

Inside the construct you have the code to make sure the fields are setup properly.

Part of the setup uses admin_menu hook, to make sure our fields are added through the add function. This function does the bulk of the work for displaying the fields. Calling the show function, based on the field we defined in our $meta_boxes array.

Another part uses the save_post action, which calls save function. This checks for correct nonce, permissions and whether it’s a new field or we’re updating an existing field.

The final hooks add your custom CSS, found inside the ‘style’ function. You can replace wp_enqueue_style and echo some CSS between <style> tags, if you only require minimal CSS.

Moving the meta box under the title

Utilising the ‘advanced’ context, the following code will place them the title. I think this is a good way to utilise the advanced context. You can add this with the rest of your meta box code.

add_action('edit_form_after_title', 'advanced_meta_boxes_under_title');

function advanced_meta_boxes_under_title()
{
  global $post, $wp_meta_boxes;
  do_meta_boxes(get_current_screen(), 'advanced', $post);

  unset($wp_meta_boxes[get_post_type($post)]['advanced']);
}

Credit: Andrew, Wordpress Stack Exchange

Using the fields in your theme

To use the custom fields within your theme, you use the get_post_meta function, within your loop.

get_post_meta takes 3 parameters

$role = get_post_meta( get_the_ID(), '_iamsteve_meta_role', true );

if (have_posts()) :
  while (have_posts()) : the_post();
    if ( ! empty($role) ) echo $role;
  endwhile;
endif;

That’s custom fields

That’s it, there is quite a bit to custom fields. I would like to explore in future posts more detailed uses of setting up the boxes with more complex fields.

Next to read

Using Wordpress customiser to create editable regions