Skip to navigation | Skip to content



Control your own WordPress custom fields

I’m currently working on a client’s WordPress site where there’s quite a few bits of custom functionality in my custom theme that rely on them entering values for posts or pages using WP’s custom fields.

Custom fields are really flexible. However, they’re not perfectly user-friendly. For instance, if no post or page is currently using a custom field that you’ve built functionality on, the user has to enter the name as well as the value of the field the first time it’s used. The drop-down of field names is dynamically gathered from the fields currently in use. Also, sometimes you want to make things easier for clients by having inline tips, and inputs that suit the field (e.g. a checkbox or select drop-down instead of just a plain text entry).

So, I set about piecing together a way to take over the Custom Fields meta box…

First off, if you’re not developing custom themes, and you want a flexible plugin that will do this sort of thing, including an easy interface for defining custom fields, what I’ve got here isn’t for you. You might want to check out Custom Field Template, Flutter, or Magic Fields. Really, the power in any of those blows the code below out the water. My concern here is some lean, only-what-you-need code that you have total control over.

Before we begin, here’s a taster. This is a grab from a page edit screen on the project I’ve developed this code for:

custom-fields

I started off working with Function’s Custom Write Panels code, but their approach ended up being most concerned with post “metadata”. It’s all stored in one field in the postmeta table, in a serialized array. Which is great if you just need a clump of different values to output for any particular post. My needs are more diverse, and I often access individual fields for various reasons.

(TIP: If you store custom fields separately, as I will, and do end up wanting to grab them all at once, use get_post_custom().)

One more shout. I don’t know PHP objects very well, I’m absorbing it as I go. My class structure for this code was taken from the very useful WordPress Plugin Template from Pressography.

OK, let’s see what we’ve got. I’ll go through it step-by-step. This code is designed to go into your custom theme’s functions.php file—though if you wouldn’t have guessed, it might not be for you! (N.B. It must not be placed into /wp-includes/functions.php—see comments below.)

if ( !class_exists('myCustomFields') ) {

	class myCustomFields {
		/**
		* @var  string  $prefix  The prefix for storing custom fields in the postmeta table
		*/
		var $prefix = '_mcf_';

First a simple check to prevent class clashes, and the definition of the first property. This is the prefix that we’ll use for the meta_key when we store values in the postmeta table.

An important point to note here is that if you put an underscore at the start of a meta_key, the custom field will be “hidden”. That means it won’t show up in the default Custom Fields meta box. This code will include the option to hide that default box anyway, but there might be situations where you want to keep that in place for other reasons, alongside your custom fields box. In this case, it’s good to keep your fields in your box with this method.

		/**
		* @var  array  $customFields  Defines the custom fields available
		*/
		var $customFields =	array(
			array(
				"name"			=> "block-of-text",
				"title"			=> "A block of text",
				"description"	=> "",
				"type"			=> "textarea",
				"scope"			=>	array( "page" ),
				"capability"	=> "edit_pages"
			),
			array(
				"name"			=> "short-text",
				"title"			=> "A short bit of text",
				"description"	=> "",
				"type"			=>	"text",
				"scope"			=>	array( "post" ),
				"capability"	=> "edit_posts"
			),
			array(
				"name"			=> "checkbox",
				"title"			=> "Checkbox",
				"description"	=> "",
				"type"			=> "checkbox",
				"scope"			=>	array( "post", "page" ),
				"capability"	=> "manage_options"
			)
		);
		/**

Now we define our custom fields. I’ve set up a little system that’s quite flexible, and included examples of three common field types. Obviously, you can extend this however you want. The field types are up to you. I’ve been using custom types that create, say, drop-downs of users of a certain role, things like that.

Anyway, the basic values set for each field are:

  • name: This gets used with the prefix to create the meta_key for the field.
  • title: This is for the form’s <label>.
  • description: This is a bit of descriptive text that goes under the field input, to help remind the user what it’s for.
  • type: The types above happen to correspond to HTML form input types, but they don’t have to.
  • scope: Where should these fields get used? Here it’s simply about whether they go on post or page screens, but again, you can make your own scopes up to restrict the fields to certain sections of your site, pages using certain templates, etc.
  • capability: Which capability is required to edit the field? If you don’t understand WordPress capabilities, Justin Tadlock has an excellent introduction to users, roles and capabilities.
		/**
		* PHP 4 Compatible Constructor
		*/
		function myCustomFields() { $this->__construct(); }
		/**
		* PHP 5 Constructor
		*/
		function __construct() {
			add_action( 'admin_menu', array( &$this, 'createCustomFields' ) );
			add_action( 'save_post', array( &$this, 'saveCustomFields' ), 1, 2 );
			// Comment this line out if you want to keep default custom fields meta box
			add_action( 'do_meta_boxes', array( &$this, 'removeDefaultCustomFields' ), 10, 3 );
		}
		/**
		* Remove the default Custom Fields meta box
		*/
		function removeDefaultCustomFields( $type, $context, $post ) {
			foreach ( array( 'normal', 'advanced', 'side' ) as $context ) {
				remove_meta_box( 'postcustom', 'post', $context );
				remove_meta_box( 'pagecustomdiv', 'page', $context );
			}
		}
		/**
		* Create the new Custom Fields meta box
		*/
		function createCustomFields() {
			if ( function_exists( 'add_meta_box' ) ) {
				add_meta_box( 'my-custom-fields', 'Custom Fields', array( &$this, 'displayCustomFields' ), 'page', 'normal', 'high' );
				add_meta_box( 'my-custom-fields', 'Custom Fields', array( &$this, 'displayCustomFields' ), 'post', 'normal', 'high' );
			}
		}

Now there’s the class’s constructor, which basically adds the action hooks for outputting the new meta box, saving values when the form’s submitted, and removing the default Custom Fields meta box if necessary.

Then there’s the functions to remove the default box, and to create the new one. In this code, it’s assumed that the new box will be there whatever on post and page screens. If you have a system where you only need it on one or the other, or have more complex conditions, you’ll need to adapt the createCustomFields() function.

		/**
		* Display the new Custom Fields meta box
		*/
		function displayCustomFields() {
			global $post;
			?>
			<div class="form-wrap">
				<?php
				wp_nonce_field( 'my-custom-fields', 'my-custom-fields_wpnonce', false, true );
				foreach ( $this->customFields as $customField ) {
					// Check scope
					$scope = $customField[ 'scope' ];
					$output = false;
					foreach ( $scope as $scopeItem ) {
						switch ( $scopeItem ) {
							case "post": {
								// Output on any post screen
								if ( basename( $_SERVER['SCRIPT_FILENAME'] )=="post-new.php" || $post->post_type=="post" )
									$output = true;
								break;
							}
							case "page": {
								// Output on any page screen
								if ( basename( $_SERVER['SCRIPT_FILENAME'] )=="page-new.php" || $post->post_type=="page" )
									$output = true;
								break;
							}
						}
						if ( $output ) break;
					}
					// Check capability
					if ( !current_user_can( $customField['capability'], $post->ID ) )
						$output = false;

Here’s the start of the output of the new custom fields box. We used wp_nonce_field() to improve security, then start our loop through all the defined fields.

The first check is on the scope of the field, which is an array of strings indicating the context in which they should be used. Outputting defaults to false, then gets set to true if any of the scope conditions are fulfilled. As I said earlier, you can define your own scopes for particular needs—the sky’s the limit.

Then there’s a quick check on the capability.

					// Output if allowed
					if ( $output ) { ?>
						<div class="form-field form-required">
							<?php
							switch ( $customField[ 'type' ] ) {
								case "checkbox": {
									// Checkbox
									echo '<label for="' . $this->prefix . $customField[ 'name' ] .'" style="display:inline;"><b>' . $customField[ 'title' ] . '</b></label>&nbsp;&nbsp;';
									echo '<input type="checkbox" name="' . $this->prefix . $customField['name'] . '" id="' . $this->prefix . $customField['name'] . '" value="yes"';
									if ( get_post_meta( $post->ID, $this->prefix . $customField['name'], true ) == "yes" )
										echo ' checked="checked"';
									echo '" style="width: auto;" />';
									break;
								}
								case "textarea": {
									// Text area
									echo '<label for="' . $this->prefix . $customField[ 'name' ] .'"><b>' . $customField[ 'title' ] . '</b></label>';
									echo '<textarea name="' . $this->prefix . $customField[ 'name' ] . '" id="' . $this->prefix . $customField[ 'name' ] . '" columns="30" rows="3">' . htmlspecialchars( get_post_meta( $post->ID, $this->prefix . $customField[ 'name' ], true ) ) . '</textarea>';
									break;
								}
								default: {
									// Plain text field
									echo '<label for="' . $this->prefix . $customField[ 'name' ] .'"><b>' . $customField[ 'title' ] . '</b></label>';
									echo '<input type="text" name="' . $this->prefix . $customField[ 'name' ] . '" id="' . $this->prefix . $customField[ 'name' ] . '" value="' . htmlspecialchars( get_post_meta( $post->ID, $this->prefix . $customField[ 'name' ], true ) ) . '" />';
									break;
								}
							}
							?>
							<?php if ( $customField[ 'description' ] ) echo '<p>' . $customField[ 'description' ] . '</p>'; ?>
					<?php
					}
				} ?>
			</div>
			<?php
		}

Now the output—pretty self-explanatory!

		/**
		* Save the new Custom Fields values
		*/
		function saveCustomFields( $post_id, $post ) {
			if ( !wp_verify_nonce( $_POST[ 'my-custom-fields_wpnonce' ], 'my-custom-fields' ) )
				return;
			if ( !current_user_can( 'edit_post', $post_id ) )
				return;
			if ( $post->post_type != 'page' && $post->post_type != 'post' )
				return;
			foreach ( $this->customFields as $customField ) {
				if ( current_user_can( $customField['capability'], $post_id ) ) {
					if ( isset( $_POST[ $this->prefix . $customField['name'] ] ) && trim( $_POST[ $this->prefix . $customField['name'] ] ) ) {
						update_post_meta( $post_id, $this->prefix . $customField[ 'name' ], $_POST[ $this->prefix . $customField['name'] ] );
					} else {
						delete_post_meta( $post_id, $this->prefix . $customField[ 'name' ] );
					}
				}
			}
		}

	} // End Class

} // End if class exists statement

// Instantiate the class
if ( class_exists('myCustomFields') ) {
	$myCustomFields_var = new myCustomFields();
}

Finally, a function to save the values when the form is submitted. The nonce is checked, then we loop through all the fields, checking that the capability is there and the field has been submitted. Then, we either add / update the field value if there is one, or delete it. You might want to just add / update, and store empty values for fields that apply to a post or page, but don’t have anything set. I thought I’d err on the side of keeping the database lean.

Then we instantiate the class, and that’s it.

The full code

Below is the code in full. To use it:

  1. Paste it into your functions.php.
  2. Change the $prefix if you want.
  3. Adapt the $customFields array to define your fields.
  4. If you’ve added your own scopes, add case statements for them around line 82.
  5. If you’ve added your own field types, add case statements for them around line 105.

I’ve been adapting certain bits as a write this post, making the bits that are specific to my use of the code more general for this example, so forgive me if there’s any bugs that have crept in. Let me know if you find any problems with the code!

if ( !class_exists('myCustomFields') ) {

	class myCustomFields {
		/**
		* @var  string  $prefix  The prefix for storing custom fields in the postmeta table
		*/
		var $prefix = '_mcf_';
		/**
		* @var  array  $customFields  Defines the custom fields available
		*/
		var $customFields =	array(
			array(
				"name"			=> "block-of-text",
				"title"			=> "A block of text",
				"description"	=> "",
				"type"			=> "textarea",
				"scope"			=>	array( "page" ),
				"capability"	=> "edit_pages"
			),
			array(
				"name"			=> "short-text",
				"title"			=> "A short bit of text",
				"description"	=> "",
				"type"			=>	"text",
				"scope"			=>	array( "post" ),
				"capability"	=> "edit_posts"
			),
			array(
				"name"			=> "checkbox",
				"title"			=> "Checkbox",
				"description"	=> "",
				"type"			=> "checkbox",
				"scope"			=>	array( "post", "page" ),
				"capability"	=> "manage_options"
			)
		);
		/**
		* PHP 4 Compatible Constructor
		*/
		function myCustomFields() { $this->__construct(); }
		/**
		* PHP 5 Constructor
		*/
		function __construct() {
			add_action( 'admin_menu', array( &$this, 'createCustomFields' ) );
			add_action( 'save_post', array( &$this, 'saveCustomFields' ), 1, 2 );
			// Comment this line out if you want to keep default custom fields meta box
			add_action( 'do_meta_boxes', array( &$this, 'removeDefaultCustomFields' ), 10, 3 );
		}
		/**
		* Remove the default Custom Fields meta box
		*/
		function removeDefaultCustomFields( $type, $context, $post ) {
			foreach ( array( 'normal', 'advanced', 'side' ) as $context ) {
				remove_meta_box( 'postcustom', 'post', $context );
				remove_meta_box( 'pagecustomdiv', 'page', $context );
			}
		}
		/**
		* Create the new Custom Fields meta box
		*/
		function createCustomFields() {
			if ( function_exists( 'add_meta_box' ) ) {
				add_meta_box( 'my-custom-fields', 'Custom Fields', array( &$this, 'displayCustomFields' ), 'page', 'normal', 'high' );
				add_meta_box( 'my-custom-fields', 'Custom Fields', array( &$this, 'displayCustomFields' ), 'post', 'normal', 'high' );
			}
		}
		/**
		* Display the new Custom Fields meta box
		*/
		function displayCustomFields() {
			global $post;
			?>
			<div class="form-wrap">
				<?php
				wp_nonce_field( 'my-custom-fields', 'my-custom-fields_wpnonce', false, true );
				foreach ( $this->customFields as $customField ) {
					// Check scope
					$scope = $customField[ 'scope' ];
					$output = false;
					foreach ( $scope as $scopeItem ) {
						switch ( $scopeItem ) {
							case "post": {
								// Output on any post screen
								if ( basename( $_SERVER['SCRIPT_FILENAME'] )=="post-new.php" || $post->post_type=="post" )
									$output = true;
								break;
							}
							case "page": {
								// Output on any page screen
								if ( basename( $_SERVER['SCRIPT_FILENAME'] )=="page-new.php" || $post->post_type=="page" )
									$output = true;
								break;
							}
						}
						if ( $output ) break;
					}
					// Check capability
					if ( !current_user_can( $customField['capability'], $post->ID ) )
						$output = false;
					// Output if allowed
					if ( $output ) { ?>
						<div class="form-field form-required">
							<?php
							switch ( $customField[ 'type' ] ) {
								case "checkbox": {
									// Checkbox
									echo '<label for="' . $this->prefix . $customField[ 'name' ] .'" style="display:inline;"><b>' . $customField[ 'title' ] . '</b></label>&nbsp;&nbsp;';
									echo '<input type="checkbox" name="' . $this->prefix . $customField['name'] . '" id="' . $this->prefix . $customField['name'] . '" value="yes"';
									if ( get_post_meta( $post->ID, $this->prefix . $customField['name'], true ) == "yes" )
										echo ' checked="checked"';
									echo '" style="width: auto;" />';
									break;
								}
								case "textarea": {
									// Text area
									echo '<label for="' . $this->prefix . $customField[ 'name' ] .'"><b>' . $customField[ 'title' ] . '</b></label>';
									echo '<textarea name="' . $this->prefix . $customField[ 'name' ] . '" id="' . $this->prefix . $customField[ 'name' ] . '" columns="30" rows="3">' . htmlspecialchars( get_post_meta( $post->ID, $this->prefix . $customField[ 'name' ], true ) ) . '</textarea>';
									break;
								}
								default: {
									// Plain text field
									echo '<label for="' . $this->prefix . $customField[ 'name' ] .'"><b>' . $customField[ 'title' ] . '</b></label>';
									echo '<input type="text" name="' . $this->prefix . $customField[ 'name' ] . '" id="' . $this->prefix . $customField[ 'name' ] . '" value="' . htmlspecialchars( get_post_meta( $post->ID, $this->prefix . $customField[ 'name' ], true ) ) . '" />';
									break;
								}
							}
							?>
							<?php if ( $customField[ 'description' ] ) echo '<p>' . $customField[ 'description' ] . '</p>'; ?>
						</div>
					<?php
					}
				} ?>
			</div>
			<?php
		}
		/**
		* Save the new Custom Fields values
		*/
		function saveCustomFields( $post_id, $post ) {
			if ( !wp_verify_nonce( $_POST[ 'my-custom-fields_wpnonce' ], 'my-custom-fields' ) )
				return;
			if ( !current_user_can( 'edit_post', $post_id ) )
				return;
			if ( $post->post_type != 'page' && $post->post_type != 'post' )
				return;
			foreach ( $this->customFields as $customField ) {
				if ( current_user_can( $customField['capability'], $post_id ) ) {
					if ( isset( $_POST[ $this->prefix . $customField['name'] ] ) && trim( $_POST[ $this->prefix . $customField['name'] ] ) ) {
						update_post_meta( $post_id, $this->prefix . $customField[ 'name' ], $_POST[ $this->prefix . $customField['name'] ] );
					} else {
						delete_post_meta( $post_id, $this->prefix . $customField[ 'name' ] );
					}
				}
			}
		}

	} // End Class

} // End if class exists statement

// Instantiate the class
if ( class_exists('myCustomFields') ) {
	$myCustomFields_var = new myCustomFields();
}

UPDATE 18/11/09: I’ve just found an important issue with using the save_post hook. Alex King explains it here. The executive summary is that when the saveCustomFields function above is added to the save_post hook, it’s now set to receive 2 arguments—the post ID, as before, plus the post’s data. Using the passed $post argument, the save function checks that the post_type is “page” or “post”. This stops the save code being run for autosaves or saving revisions, which could create duplicate postmeta fields.

51 comments

  1. flavius (13th November 2009)

    hi,

    how can you create a dropdown in the metabox?

    thanks.

  2. Steve Taylor (13th November 2009)

    Same as you would in any HTML page. Not sure what the problem would be?

  3. flavius (13th November 2009)

    well, i’d like to use it like this:


    var $seoFields = array(
    array(
    "name" => "color-change",
    "title" => "Change the theme:",
    "description" => "(Pick the color scheme you want to use)",
    "options" => array( "Blue", "Red", "Black" ),
    "type" => "dropdown",
    "scope" => array( "post" ),
    "capability" => "edit_posts"
    ));

    but i don’t know how to save it. and when displaying it’s contents in the meta box, it only shows _mcf_Array.

    i was thinking of exploding the array, to populate the dropdown but still, i wouldn’t know how to save it.

  4. Steve Taylor (13th November 2009)

    As long as you’ve added a dropdown case to the switch ( $customField[ 'type' ] ), you should be fine. There’s no issue with saving it: when you submit the form, the select field’s value will be that of the selected option, and that’ll get saved in the meta field.

  5. flavius (13th November 2009)

    please, delete my above comments.
    here’s my switch code: switch code

    any idea how to remove the duplicate value? one is being read from the array and the other one is the one previousely saved.

    thanks.

  6. Steve Taylor (13th November 2009)

    Not sure what was stripped – maybe use http://pastebin.ca/ for long snippets?

  7. flavius (13th November 2009)

    uh… i already used pastebin and posted a comment with the link here, maybe it was marked as spam?

    anyway, i solved the problem.

    thanks a lot for this great article (A LOT better than others i found on the web), been a real help. keep up the great work!

  8. Steve Taylor (13th November 2009)

    Sorry, you got spammed! I’ve tidied things up. Glad you got it sorted, and glad the code was useful! My version has expanded immensely since I posted this – it can be really powerful. I’m sure the plugins I mentioned are even better, but I’m a sucker for rolling my own ;-)

  9. flavius (13th November 2009)

    i really don’t wanna use plugins, especially when developing themes for other people no-so-into-computers. easier for them to have everything laid out for them.

    besides, i really like it to make my own custom things, and with my knowledge and the help of guys like you, i do a pretty good job :)

    again, thanks!

  10. Jason (14th November 2009)

    How can I show the existing custom fields already being used by my theme?
    Before adding this code to my functions.php file I had 3 custom fields that are no longer visible.

  11. Steve Taylor (14th November 2009)

    You could just comment out line 48, which adds the action to remove the default custom fields box. If you use field names with an underscore at the start, the fields this code uses won’t appear in the default box – where you’ll still be able to manage the rest of your custom fields.

  12. sebastien (14th November 2009)

    Superbe article ! Très professionnel, très bien documenté ! Merci. Tu expliques très bien qu’on peut limiter l’affichage d’un champs aux articles ou aux pages. mais est-il possible de limiter l’affichage d’un champ aux articles d’une catégorie spécifique ? Par-exemple, si on ne veut afficher une checkbox seulement si le post est dans la catégorie “portfolio”.

    merci !
    —-

    Very nice post ! Very professional, very well documented ! Thank you. You explain very well that we can limit the display of a custom field in posts or pages. But is it possible to limit the display of a field to posts with a specific category? For example, if i want display a checkbox only if the post is in the category “portfolio”.

    Thanks !

  13. sebastien (14th November 2009)

    with your code it’s not possible to update the checkbox. What can i do to update a checkbox ?

  14. Steve Taylor (14th November 2009)

    Regarding checkboxes – works fine for me.

    And regarding limiting the display of a field, you can just customize the “scope” system. Maybe add “portfolio” to the scope array for the definition of that checkbox field. Then, in the scope check around line 82 add a case for “portfolio” where you make sure that the post being edited is in that category. You could add an extra check in the save function, but it’s not much of a risk to leave that out.

  15. sebastien (15th November 2009)

    Thanx Steve, and for the checkbox, what’s about the update ?

  16. sebastien (15th November 2009)

    Excuse-moi, je n’avais pas vu que tu avais écrit “Regarding checkboxes – works fine for me.”.
    Pour la checkbox, tu as écrit dans le code : value=”yes”. Donc la valeur est toujours = “yes”. Elle ne peut donc pas être false. La case est donc toujours cochée.

    Excuse me, I did not see that you had written “Regarding checkboxes – works fine for me.”.
    For the checkbox, you wrote in the code: value = “yes”. So the value is always = “yes”. It can not be false. The box is still checked, isn’t it ?

  17. Steve Taylor (15th November 2009)

    Sorry Sebastien, you’re right. As I said, my own version has evolved, and the above was abstracted from it to make it more general—obviously this error crept in as I adapted it for the post. Ah, the perils of not wrapping code up as a plugin!

    It was the final bit saving the fields that was wrong, around line 145. I’ve changed it now. It should delete the field if it’s not set. Simple yes / no checkboxes will have the value as “yes”, because if it’s not checked, the field simply isn’t set in the post data. You could tweak the code to test for checkboxes if you want, and update the field with “no” if the checkbox field isn’t set. Or maybe add a radio button type, and use two paired radio inputs, one “yes” and one “no”. As it is, when I’m using the data, I’m counting the absence of the checkbox field in the postmeta as a “no”.

    Anyway, thanks for reporting the bug!

  18. Steve Taylor (18th November 2009)

    To anyone subscribed to comments here, please check the note at the end of the post about the recent update to the code.

  19. billy (16th December 2009)

    Very nice. We need more tutorials about this and making custom theme options.

  20. billy (16th December 2009)

    In wordpress Version 2.8.6 it does not seem to add the field when you update the page or post.

  21. Steve Taylor (16th December 2009)

    I’ve got it working on 2.8.6 OK – though because this isn’t a plugin, my version has developed a lot to cater for the site I’m on.

    Do post your issue if you can be more specific (do a bit of debugging yourself and tell me if you get stuck at a part of the above code you don’t understand, or you think should be working but isn’t).

    If you’re not very experienced with PHP or WP development, maybe try one of the plugins I mention near the beginning? Good luck!

  22. billy (16th December 2009)

    I would rather do it this way.

    /* remove_meta_box( ‘postcustom’, ‘post’, $context ); */
    /* remove_meta_box( ‘pagecustomdiv’, ‘page’, $context ); */

    I comment out the above code so I can see if it adds the field “block-of-text” but it does not for some reason.

  23. Steve Taylor (17th December 2009)

    Billy, not sure of your problem. Those lines of code are to remove the default Custom Fields box and replace it entirely with the new one. If you comment them out, I guess you’re getting the default Custom Fields box, plus the new one. I guess this might cause clashes between the fields? Does it work when you leave the above lines in?

  24. billy (17th December 2009)

    I put this in the theme file and nothing shows up

    http://www.pastebin.ca/1717590

  25. Steve Taylor (17th December 2009)

    Firstly, it looks like you’ve done something I sometimes do – you’ve copied the get_post_meta arguments from the function itself, and left $single = true. That looks like a function’s default for the argument. Calling the function, you just pass true or false.

    If that doesn’t fix it, pay attention to this line in the above code:

    var $prefix = '_mcf_';

    Read about it under the first code snippet here. Double-check whether the field has been stored by checking the postmeta table via phpMyAdmin. If the field is there, I guess either the above problem with the get_post_meta call is an issue, or you’re not including the prefix in the custom field key – or both?

  26. billy (17th December 2009)

    if i remove that line var $prefix = ‘meta_key’;
    it works

  27. Steve Taylor (17th December 2009)

    Do remember the bit about the meta_key starting with an underscore though.

    It’s also worth having a prefix for your theme / plugin (whether there’s an underscore or not), to prevent clashes with other plugins.

  28. billy (17th December 2009)

    oops I meant I removed this var $prefix = ‘_mcf_’;

  29. David (17th December 2009)

    Steve, great work on the post. I, like the first commenter, am trying to implement a select box with certain options and an associated value for each option. I created a new case for dropdown, and added values for the options and the value in $customFields array, but still no luck. The only option I get is the _mcf_Array. thoughts?

    ds

  30. Steve Taylor (18th December 2009)

    Hi David, could you post the code on pastebin.ca?

  31. RIVANX (7th January 2010)

    thanks for info…. good job

  32. Newbie (12th January 2010)

    Per the 18/11/09 update, what needs to be edited exactly to fix this bug?

  33. Steve Taylor (12th January 2010)

    It’s edited – the full code snippet above includes the fix. Check lines 46 & 145.

  34. paul (12th January 2010)

    any ideas on how to add an image upload custom field?

  35. Steve Taylor (12th January 2010)

    Paul, I’ve not tried that myself, but I’m sure one of the plugins that tackle this include upload fields.

  36. paul (13th January 2010)

    Steve

    Yes they do, but I want to do this without a plugin, and I would have thought it’s a common request but I can’t find any info about it anywhere
    thanks anyway
    paul

  37. George (17th January 2010)

    Some more hints on how to make dropdown ?
    ( http://sltaylor.co.uk/blog/control-your-own-wordpress-custom-fields/#comment-2469 )

    Cheers.

  38. Steve Taylor (18th January 2010)

    George, I wasn’t sure what problem that person talking about the drop-down had. You can see the comments I offered to help, not sure that helped, but they seemed to solve it. Basically, I think adapting the above code to have a dropdown instead of a text field is nuts-and-bolts XHTML / PHP. Do post a specific issue if you want (use pastebin.ca for code snippets), but I can’t do an XHTML / PHP tutorial here :-)

  39. George (19th January 2010)

    I’ve tried harder to get it working so here is the final code if someone else is feeling unsure: http://www.pastebin.ca/1757928

  40. Steve Taylor (19th January 2010)

    George, thanks for posting the code. What’s the problem exactly? Actually, second thoughts, was there a problem, or is this the “working code”?

  41. George (20th January 2010)

    Steve,
    Hm actually no, it’s not final working code,
    later on I’ve added default option and the very important line:

    if ( get_post_meta( $post->ID, $this->prefix . $customField['name'], true ) == $key ) $selected = "selected=\"selected\"";

    This will give “selected” state on the chosen option after the page has been reloaded once again.

    Final (yes – working ) code:

    http://www.pastebin.ca/1759197

    Cheers.

  42. Csabbencs (24th January 2010)

    Hi, I got this: Fatal error: Call to undefined function add_action() in … \wp-includes\functions.php … I have PHP 5.3.0. Could you please help? I copied the ‘full code’ part without any change into functions.php.

  43. Steve Taylor (24th January 2010)

    Csabbencs, as stated in the post, this code goes into the functions.php file of a customized theme. If you’re not happy customizing themes, it’s not for you—check out the plugins mentioned. If you are, put the code into /wp-content/themes/[your-theme]/functions.php.

    I’ve altered the post to emphasize this more, it seems to be a common mistake for people to put theme code into /wp-includes/functions.php. As a general rule, you should never alter WP core files like this. All customization should be via themes and plugins. If you need some customization that can’t be done with these, then… well, if you’ve decided that, you probably know what you’re doing and it’s nice that WP’s core is open. But generally—leave WP core code alone :-)

  44. Csabbencs (24th January 2010)

    My mistake. Thanks, Steve. Now, I just don’t understand myself why I thought of putting it in the core function.php when I totally agree with you that the core should not be touched unless someone wants to go mad when upgrading. :)

  45. Stephen Heyes (25th January 2010)

    I can’t explain how excited I was to find a solution for a big conflict I was getting using the plugins More Fields & Adminimize!!! So thanks very much!

    I just pasted the code as you said and Voila!!! It worked!! I now have a second custom fields box :)

    Now I will admit I’m new to php but have enough knowledge to get this workign so far :) but if I ask something really dumb I do apologizes.

    So my 1st question would be. How can I use your code to pull or update all the custom fields I’ve already setup with the more More Fields plugin? I’ve deactivated it, and I can see all the date in the standard custom fields box used by wordpress, I was hoping that if I created the same fields, named them the same in the array they would show..

    My 2nd qustion, if there isn’t a solution to my 1st question, and i have to go and transfer all the date into the new custom field box added by your code, what code would I use to extract the info I need into my theme template?

    Below is an example of the code I was using to get the info when using the More Fields plugin….


    $value )
    echo $value;
    ?>

    Hope you can help, thanks for your time in advance!

  46. Steve Taylor (25th January 2010)

    Stephen, glad this code helped you out! I don’t know the More Fields plugin, but if it just managed standard custom fields, this code should be able to take those fields over if you add them to the $customFields array. The standard WP custom fields box is designed to output anything that’s been set for the current post (as long as it’s not hidden using the underscore prefix). The output of the above code is governed by what fields you’ve pre-defined—it really came about as a result of my wanting to control the fields that a client used. So, you have to manually specify which fields are to be managed.

    If More Fields stores anything outside the standard WP custom fields system, you’ll have to whip up some kind of PHP export / import script.

  47. miklb (25th January 2010)

    Very nice tutorial, and just what I was looking for. One problem I’m seeing is that I’m getting a “Undefined index: my-custom-fields_wpnonce” as well as a header already sent warning. Not sure if it’s happening on an autosave or not. The undefined index is happening on the second reference to the wpnonce. Using 2.9.1.1. Any tips appreciated.

  48. Steve Taylor (26th January 2010)

    miklb, I’ve not seen this error before. Autosaves should be excluded as per the note at the end. I developed this code on 2.8, and actually I’ve not tried it on 2.9 yet (the client wants to stick to 2.8.6 until they have to upgrade due to security). Maybe an issue with 2.9? I’ll post if I run into the same thing when I upgrade—let me know if you solve this.

  49. miklb (26th January 2010)

    Steve, thanks for the quick reply. I’m not sure of the ramifications, but I simply commented that line out and haven’t received either error again in testing. I’m also not sure of the ramifications of not using a prefix, and just using the existing custom fields I had, but otherwise, it’s a great example of code, and perfect for what I had originally set out to accomplish. Cheers!

  50. Stephen (27th January 2010)

    Hey Steve, Sorry to bother you once again!

    I’ve created all the fields I want, but now all the fields show on every post-page.php. I’ve tried a whole bunch of combintions using the scope, but can’t seem to get them to work. I tried creating new scopes and just adding page-new.php?cat=3 to the if statement but that doesn’t seem to work….

    I would really appreciated it if you could give me an example of how I could make the custom fields appear for only one category. What can or should I be looking for that makes each page unique. In your instructions you talk about showing fields for only certain page templates but I really need it to work by category.

    I’ve used a plugin called “Category Write Panels” so in my left hand menu I have a list of all my Categories each with there own “Edit” and “And New” links.

    Please help :(

    Thanks for your time in advance

  51. Steve Taylor (27th January 2010)

    I’ve not restricted the code to one category before, but it should be doable with some digging around with the $post object. The only thing is that of course you wouldn’t be able to display them until a post is created (and the category set).

Comments here are now closed.

Please note: The code in this post has become quite popular, and unfortunately I'm not finding time to answer all the questions about it. If I find time to support it, I may wrap it up into a plugin - but then, as mentioned at the start of the post, there are already good plugins doing this job. It's great the code has helped some people out. If you're relatively confident with PHP and WordPress, and find bugs in the above code, please contact me. However, I'm sorry to say I won't be able to answer questions about modifying the code for different purposes, interacting with different plugins, etc. Thanks for your understanding.

Recent posts

Archives