$post->ID not working in combination with a custom query

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>';
}

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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>' ); ?>">. 🙂

Method 2

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.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x