Question

What is the reason that $post->ID is not working in metabox_callback after adding a custom query. How can I avoid this? Should I use $post_id = isset( $_GET['post'] ) ? $_GET['post'] : ( isset( $_POST['post_ID'] ) ? $_POST['post_ID'] : false ); instead of $post->ID?

Similar question/same situation here.

What is the reason for problems like this and how can you fix it the proper way?

EDIT:

function custom_add_meta_boxes() {
    add_meta_box (
        'enroll_user',
        'Enroll users',
        'enroll_user_callback',
        'group',
        'normal'
    );  

    add_meta_box (
        'module_group',
        'Assign modules',
        'module_group_callback',
        'group',
        'side'
    );
}

add_action( 'add_meta_boxes', 'custom_add_meta_boxes' );

function enroll_user_callback ( $post ) {
    wp_nonce_field( 'enroll_user_save', 'enroll_user_nonce' );
        
    echo '<label for="enroll_user_field">Enroll users</label><br /><br />';
    
    echo '<select style="width: 50%; min-width: 250px;" multiple name="enroll_user_field[]" id="enroll_user_field">';
    
        $users = get_users( array( 'fields' => array( 'ID', 'user_email' ) ) );
        
        foreach( $users as $user ){
            $enrolledusers = get_user_meta($user->ID, '_enroll_user_value_key_' . $post->ID , false);
            
            if( in_array($post->ID, $enrolledusers )) {
                echo '<option selected value="' . $user->ID . '">' . esc_attr( $user->user_email ) . '</option>';
            } else {
                echo '<option value="' . $user->ID . '">' . esc_attr( $user->user_email ) . '</option>';
            }
        }
    echo '</select>';
    
    // This is posting a different ID after adding my query in module_group_callback
    print_r($post);
}

function module_group_callback ( $post ) {
    wp_nonce_field( 'module_group_save', 'module_group_nonce' );
        
    echo '<label for="module_group_field">Assign modules</label><br /><br />';
    
    $args = array(
        'post_type' => 'module',
        'orderby' => 'menu_order',
        'order' => 'ASC',
        'posts_per_page' => -1,
    );
    
    $query = new WP_Query( $args );
    
    echo '<select style="width: 100%;" multiple name="module_group_field[]" id="module_group_field">';
        while ( $query->have_posts() ) : $query->the_post();
            $moduleid = $query->post->ID;
            echo '<option value="' . $moduleid . '">' . esc_attr( get_the_title( $moduleid ) ) . '</option>';
        endwhile;   
    echo '</select>';
}
Was it helpful?

Solution

That $query->have_posts() modifies the global $post variable which is also used on the admin side, and on the front-end/public side of the site, you would simply call wp_reset_postdata() to restore the global $post back to the post before you call the $query->have_posts().

But on the admin side, you need to manually restore the variable like so, after your loop ends:

function module_group_callback( $post ) {
    ...
    while ( $query->have_posts() ) : $query->the_post();
        ...
    endwhile;

    // Restore the global $post back to the one passed to your metabox callback.
    $GLOBALS['post'] = $post;
}

But if you call global $post in your code, then yes, you can use the backup method:

function module_group_callback( $post ) {
    global $post;   // this is required
    $_post = $post; // backup the current $post variable in the global scope

    ...
    while ( $query->have_posts() ) : $query->the_post();
        ...
    endwhile;

    $post = $_post; // restore
}

Alternatively, you could just omit the $query->have_posts() and loop manually through $query->posts:

function module_group_callback( $post ) {
    ...
    // Don't use $post with the 'as'. Instead, use $p, $post2 or another name.
    foreach ( $query->posts as $p ) {
        echo '<option value="' . $p->ID . '">' . esc_attr( get_the_title( $p ) ) . '</option>';
    }
}

Either way, if your code modifies the global $post, then make sure to restore it later.

And btw, you would want to use esc_html() there, because esc_attr() is for escaping attribute values, e.g. <input value="<?php echo esc_attr( 'some <b>HTML</b>' ); ?>">. :-)

OTHER TIPS

A possible solution is to save the $post variable before the query, like: $originalpost = $post. After the query you set the it back $post = $originalpost. I don't know of this is valid solution. But it seems to work.

Thanks to @Jamie, here.

Licensed under: CC-BY-SA with attribution
Not affiliated with wordpress.stackexchange
scroll top