1
\$\begingroup\$

I have created a basic checkbox custom meta box in order to set a post as "featured".

//create the boxes html markup
function custom_meta_box_markup($object)
{
    wp_nonce_field(basename(__FILE__), "meta-box-nonce");

    ?>
        <div>

            <label for="meta-box-checkbox">Featured Article </label>
            <?php
                $checkbox_value = get_post_meta($object->ID, "meta-box-checkbox", true);

                if($checkbox_value == "")
                {
                    ?>
                        <input name="meta-box-checkbox" type="checkbox" value="true">
                    <?php
                }
                else if($checkbox_value == "true")
                {
                    ?>  
                        <input name="meta-box-checkbox" type="checkbox" value="true" checked>
                    <?php
                }
            ?>
        </div>
    <?php  
}


function add_custom_meta_box()
{
    add_meta_box("demo-meta-box", "Featured Article", "custom_meta_box_markup", "post", "side", "high", null);
}

add_action("add_meta_boxes", "add_custom_meta_box");
// save checkbox meta 
function save_custom_meta_box($post_id, $post, $update)
{
    if (!isset($_POST["meta-box-nonce"]) || !wp_verify_nonce($_POST["meta-box-nonce"], basename(__FILE__)))
        return $post_id;

    if(!current_user_can("edit_post", $post_id))
        return $post_id;

    if(defined("DOING_AUTOSAVE") && DOING_AUTOSAVE)
        return $post_id;

    $slug = "post";
    if($slug != $post->post_type)
        return $post_id;

    $meta_box_checkbox_value = "";

    if(isset($_POST["meta-box-checkbox"]))
    {
        $meta_box_checkbox_value = $_POST["meta-box-checkbox"];
    }   
    update_post_meta($post_id, "meta-box-checkbox", $meta_box_checkbox_value);
}

add_action("save_post", "save_custom_meta_box", 10, 3);

Now this code does work rendering a checkbox in the post editor, and saving the meta information for use within the theme. The issue I have is this creates several rows in the DB (seems like it could be a performance issue down the road) and more importantly, there can be several "featured" posts, when there should be only one. What is the most efficient way to maybe consolidate this meta field into only 1 db row AND check and erase any existing featured posts during the save of the current featured post?

\$\endgroup\$
2
\$\begingroup\$

I'd do your code like this. It's a bit cleaner imo, and it has translatable strings in it, if you ever want to translate your theme/plugin:

add_action('add_meta_boxes', 'add_custom_meta_box');

function add_custom_meta_box() {
    add_meta_box('demo-meta-box', esc_html__('Featured Article', 'your_theme_slug'), 'custom_meta_box_markup', 'post', 'side', 'high', null);
}

//create the boxes html markup
function custom_meta_box_markup() {
    global $post;
    $checkbox_value = get_post_meta($post->ID, 'meta-box-checkbox', true);
    wp_nonce_field(basename(__FILE__), 'meta-box-nonce');
    ?>
        <div>
            <input id="meta-box-checkbox" type="checkbox" name="meta-box-checkbox" value="1" <?php checked( $checkbox_value, 1); ?>/>
            <label for="meta-box-checkbox"><?php esc_html_e('Featured Article', 'your_theme_slug') ?></label>

        </div>
    <?php
}

// save checkbox meta
function save_custom_meta_box($post_id) {
    if (!isset($_POST['meta-box-nonce']) || !wp_verify_nonce($_POST['meta-box-nonce'], basename(__FILE__)))
        return $post_id;

    if(!current_user_can("edit_post", $post_id))
        return $post_id;

    if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
        return $post_id;

    if( isset( $_POST['meta-box-checkbox'] ) ){
        update_post_meta( $post_id, 'meta-box-checkbox', true );
    } else{
        update_post_meta( $post_id, 'meta-box-checkbox', false );
    }
}

add_action('save_post', 'save_custom_meta_box', 10, 3);

Now, as for creating several rows in the database. It creates one additional field per post in the wp_postmeta table, because you are storing your value in a post meta. This is ok behavior, and shouldn't have any adverse performance effects on your website.

Now what you want is to automatically delete this post meta, if the same is set on a different post. The first thing that comes to my mind is a custom SQL query placed on save.

First you'd need to check to see if any of the posts have this field

global $wpdb;
$wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE meta_key = 'meta-box-checkbox' AND meta_value = 1", ARRAY_A);

This query will return array of all posts that have your meta-box-checkbox set to 1.

You'll have to check if the current post id is the same as the one in this query, if it is, you can continue to save, and if it isn't you'd remove (with SQL query) this entry from the postmeta table. Also you could only check if the meta key exists, and if the post id is different, remove it. This will ensure the deletion of the database entry even if the value is 0 (unchecked checkbox).

The other thing that comes to mind is delete_post_meta(). But I think you'd still need to sieve through posts with this post meta key, see if the key exists and the post id's are different, and then delete it with delete_post_meta().

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.