Support » Developing with WordPress » changing a post status manually

  • Resolved L D

    (@enochfung)


    Hi, I’m trying to change the post status manually. I’ve tried using wp_transition_post_status(), wp_update_post(), and I’ve even tried $post->post_status method. My code currently looks like this:

    
    foreach ( $post_ids as $post ) {
    	$old_status = $post->post_status;
    	error_log($old_status);
    	$post->post_status = 'draft';
    
    	wp_transition_post_status( 'draft', $old_status, $post );
    
    	error_log(get_post_status($post));
    }
    

    The error log output is this:

    
    [30-Nov-2017 03:45:46 UTC] 
    [30-Nov-2017 03:45:46 UTC] publish
    

    I don’t understand why $post->post_status doesn’t work to get or to assign a status but get_post_status() does (and what would be its equivalent to assign a new status??)? Could use a pointer to what I’m doing wrong!

Viewing 4 replies - 1 through 4 (of 4 total)
  • In your foreach you’re referring to $post_id. This implies that $post will just be the ID not a post object. That means that it won’t have a post_status property. To get properties of the post with -> you need $post to be a post object, not just an ID. You will also need a post object to use wp_transition_post_status() properly.

    The solution is to either change however you’re getting $post_ids to get post objects, or inside your foreach loop turn the ID into an object with get_post():

    foreach ( $post_ids as $post_id ) {
    	$post = get_post( $post_id );
    	
    	wp_transition_post_status( 'draft', $post->post_status, $post );
    }
    
    L D

    (@enochfung)

    Hi Jacob, thanks for responding. I have been trying different ways and I came upon a way that worked. Your explanation helps to explain a bit why it worked but I’m not understanding it fully because I tried your method and it didn’t work. So I’m a bit confused as to whether I am converting the ID to object (and vice versa) correctly. Right now, as it stands, it works but it is because I basically copied and pasted code. Can you review my thought-process and correct my understanding?

    Here is the code that works:

    
    foreach ( $post_ids as $post_id ) {
    	// Unhook to prevent infinite loop
    	remove_action( 'save_post', __FUNCTION__ );
    
    	$args = array(
    		'ID'             => $post_id,
    		'post_status' => 'archive',
    	);
    
    	wp_update_post( $args );
    
    	// Add hook back again
    	add_action( 'save_post', __FUNCTION__, 10, 3 );
    }
    

    Here is what I tried from the example you provide:

    
    foreach ( $post_ids as $post_id ) {
    
    		// convert string ID to object by passing ID to get_post()
    		// $post will now receive post object based on the ID passed
    		$post = get_post( $post_id );
    		
    		// get the status from $post object using -> . Assign the value to $oldstatus (string).
    		$oldstatus = $post->post_status;
    		error_log($oldstatus);
    		
    		// assign a new status to the object $post then place the status into $newstatus string
    		$post->post_status = 'draft';
    		$newstatus = $post->post_status;
    		error_log($newstatus);
    		
    		// pass the statuses into wp_transition_post_status. $newstatus, $oldstatus are strings. $post is the object we want to "transition"
    		wp_transition_post_status( $newstatus, $oldstatus, $post );
    		
    		// see if the object was actually modified
    		error_log(get_post_status($post));
    		// see if the original post ID was actually modified
    		error_log(get_post_status($post_id));
    }
    

    Output:

    
    [30-Nov-2017 13:46:17 UTC] publish
    [30-Nov-2017 13:46:17 UTC] draft
    [30-Nov-2017 13:46:17 UTC] draft
    [30-Nov-2017 13:46:17 UTC] publish
    

    From this, I am seeing that there are two different objects being modified. I am reading this: https://developer.wordpress.org/reference/functions/get_post/ . Am I correct to say that because get_post() returns an instance of the actual post object and therefore, I am not actually modifying the original post object? And as a result, I “see” that the original post object is not modified but this “copy” of the object is being modified. Is this the correct understanding?

    For the first set of code that works, is it because wp_update_post() modifies the actual post object? However, I don’t understand the unhook stuff… I just copied and pasted it from someone’s user contributed notes… =/

    Edit: Sorry, I pasted the wrong output. Corrected output.

    • This reply was modified 1 year, 11 months ago by L D.
    Moderator bcworkz

    (@bcworkz)

    Hi L D,

    You are correct, objects returned by get_post(), and objects passed in general, are always copies. You must explicitly update the object in the DB for changes to persist. wp_update_post() does this, but wp_transition_post_status() does not. It only fires other hooks that should fire when a post is updated through other means, it does not update data by itself. It may be that one of the hooks saves meta data or something, but in the default state, nothing happens by calling wp_transition_post_status(). Nothing is hooked to any of the actions.

    There are some instances where the object passed is a reference to the actual object in memory. The query passed in “pre_get_posts” for example. Changes to this passed object affect the actual query object in memory, it’s not local to your callback like it usually is, as when you hook “save_post” or use get_post(). In these latter and in most cases the object you have is local to your callback. You have to pass it to another function to cause it to be saved. If you do not save, when you exit your function, that copy disappears.

    The save post situation and most situations are referred to as “pass by value”, where you are working with a copy. The query example is referred to as “pass by reference” where you are changing the original object in memory. When you exit your function, the referenced object still exists in memory. Objects passed by value disappear. If you want to save an object passed by reference in the DB, you still must explicitly do this. All this passing only refers to what happens in memory, not the DB.

    I hope that makes sense. It’s sort of confusing unless you understand the underlying mechanics. I didn’t get it until I learned C where one deals with memory pointers.

    The save_post action callback has to remove itself from the stack because it will call wp_update_post(), which fires “save_post”. If it is not first removed, the same callback will again call wp_update_post(), which again fires “save_post”, which calls wp_update_post(), which fires…. trouble 🙂

    L D

    (@enochfung)

    Thanks, bcworkz. I will need to read that a few more times but I think I am getting the gist of it. More practice is needed!

Viewing 4 replies - 1 through 4 (of 4 total)
  • The topic ‘changing a post status manually’ is closed to new replies.