Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I am trying to create a post (well, in the end, multiple posts) from a metabox using the 'save_post' hook. I executed the script once, creating 3900 or so new posts before I crawled into posts.php and discovered that wp_insert_post() calls the 'save_post' hook. Does anyone have a creative solution around this to create the post without doing a direct db insert (the less hacky the solution is the better).

Code

add_action( 'add_meta_boxes', 'my_metabox_init' );
add_action( 'save_post', 'my_metabox_save' );
function my_metabox_init()
{
    add_meta_box(
        'my-metabox',
        'My Metabox',
        'my_metabox_render'
        'post',
        'normal',
        'core'
    );
    //enquque scripts and styles
}
function my_metabox_render() {
    //generate datasets
}
function my_metabox_save() {
    //data authenticity check
    //process & sanitize data

    //create posts
    for( $i=0; $i<$count; $i++ ) {
        $args = array(
            'post_status' => 'pending',
            'post_title'  => $_POST['post_title'][$i],
            'post_type'   => 'custom_post_type',
        );
        foreach( $category_array[$i] as $category ) {
            $args['tax_input']['custom-taxonomy'][] = $category;
        }
        if( $_POST['id_of_previously_created_post'][$i] != '' ) {
            $args['ID'] = $_POST['id_of_previously_created_post'][$i];
            unset( $args['post_status'] );
        }
        $new_post_id = wp_insert_post( $args );
    }
}

Thanks!

share|improve this question
    
can you please explain better ? why do you want to create thousands of posts without saving them ? where do you want them to be ?? –  krembo99 Jan 6 '12 at 0:55
    
It created 3900 posts unintentionally, they were all duplicates of each other because in the function which I hooked to 'save_post', I called wp_insert_post() which has a do_action('save_post') call in it, so the program got stuck in an infinite loop of post creation until the script timeout hit. I'm looking to use a native wordpress function to create a post (with a custom post type) using the 'save_post' hook. Does that make my situation any clearer? –  m0r7if3r Jan 6 '12 at 1:27
    
your situation is clear . what is not clear to me is why you want to create a post without putting it inside the db ? do you want it to only SHOW on the editor every time ? wp_insert_post() is , like the name , inserting the post to the db (As it should). do you mean you want to programatically create a post from variables ? if so , creating 3900 posts is not the hook problem, but something in your function loop, or it´s location for execution. if you will post the code it would be more clear , at least to me . –  krembo99 Jan 6 '12 at 1:35
    
The issue is, I'm relatively certain, that I am doing an add_action( 'save_post', 'my_plugin_save' ) and my_plugin_save() is calling wp_insert_post(), which in turn calls do_action( 'save_post' ), thus creating a loop. What I need to do to resolve this is either use a different hook, or use a different method of inserting into the $prefix.posts table...or potentially, I suppose, prevent save_post from making multiple passes. What code would you like to see? (this section is 200-300 lines, I can probably cut it to 50 or so) –  m0r7if3r Jan 6 '12 at 2:37
    
Could you provide a bit more context? What situation is creating the need to insert posts on save? If you are creating unique posts, you can first check if the post exists before running wp_insert_post() Should prevent it from getting stuck in a loop. I've done something similar when creating posts based on the main/first/original post's meta. –  Kailey Lampert Jan 6 '12 at 5:58

3 Answers 3

Update

The answer is so simple, I couldn’t see it at first. :)

Just remove the action during the first function call. This way, your work within the API, and your function is really called just once. No need for static or even global variables or constants.

function my_metabox_save() {
    remove_action( 'save_post', 'my_metabox_save' );
    // go on with your function ...

I’ll leave the old answer to illustrate how awkward a solution may get if you think too abstract …

Old answer

Add a check to my_metabox_save() to prevent a second call.

Sample code (not tested):

function my_metabox_save() {
    static $done = FALSE;
    if ( $done )
    { // No, not again!
        return;
    }
    //data authenticity check
    //process & sanitize data

    //create posts
    for( $i=0; $i<$count; $i++ ) {
        $args = array(
            'post_status' => 'pending',
            'post_title'  => $_POST['post_title'][$i],
            'post_type'   => 'custom_post_type',
        );
        foreach( $category_array[$i] as $category ) {
            $args['tax_input']['custom-taxonomy'][] = $category;
        }
        if( $_POST['id_of_previously_created_post'][$i] != '' ) {
            $args['ID'] = $_POST['id_of_previously_created_post'][$i];
            unset( $args['post_status'] );
        }
        $new_post_id = wp_insert_post( $args );
    }
    $done = TRUE; // Remember that we’re done.
}
share|improve this answer
    
I just tried this (and I had tried a slightly different implementation before, forgot to mention, apologies) and it did not work. Thanks for your answer. –  m0r7if3r Jan 7 '12 at 23:58
    
Try exactly this approach. It works. –  toscho Jan 8 '12 at 3:31
    
I did try this, it had the same effect as if it were not there (whole BUNCH of posts). The script cannot even run to completion, it hits the loop on do_action( 'save_post' ) within /wp-includes/post.php and then it starts my_metabox_save() again. It will never even make it out of the //create posts for() loop, it just nests them. I'm currently trying a post-title comparison method, I'll update in the morning. –  m0r7if3r Jan 8 '12 at 3:41
up vote 1 down vote accepted

I figured it out, what I ended up doing was as follows:

function my_metabox_save() {
    //do this just once
if( MY_DOING_SAVE != 'my_doing_save' ) {
    define( MY_DOING_SAVE , 'my_doing_save' );

    //data authenticity check
    //process & sanitize data

    //create posts
    for( $i=0; $i<$count; $i++ ) {
        $args = array(
            'post_status' => 'pending',
            'post_title'  => $_POST['post_title'][$i],
            'post_type'   => 'custom_post_type',
        );
        foreach( $category_array[$i] as $category ) {
            $args['tax_input']['custom-taxonomy'][] = $category;
        }
        if( $_POST['id_of_previously_created_post'][$i] != '' ) {
            $args['ID'] = $_POST['id_of_previously_created_post'][$i];
            unset( $args['post_status'] );
        }
        $new_post_id = wp_insert_post( $args );
        }
    }
}

The reason @toscho's method did not work was because of the scope of the variable, he was on the right track for sure though.

share|improve this answer
    
You probably missed the static keyword in your test. You don’t need something in a global scope, just in your function. –  toscho Jan 12 '12 at 17:42
    
No, I did use the static keyword...it's just not in a class, so that didn't work :P I wasn't really thinking when I tried it, I just sorta did it blindly, and I'm not as strong as I would like to be at OOPHP, so it didn't raise a flag for me. Thanks again for your help. –  m0r7if3r Jan 12 '12 at 17:45
    
The static keyword in my example is not OOP, it just makes sure the value is available in all calls to this function. See the manual. Make sure your constant never interferes with other code. The global scope of a constant is one of the major drawbacks for constants, that’s why I avoid them whenever possible. –  toscho Jan 12 '12 at 17:58
    
Ah, sweet...learn something new every day! In that case though, the issue with your solution was that static was being set every time the function was called, rather than being set only the first time...I'm actually going to try to implement this using static, I'll update the answer if it works. –  m0r7if3r Jan 12 '12 at 22:25
    
Nope, tried it and it did not work...I'm not entirely sure why (I just did a one for one replace with the static variable). If you have any ideas as to why, I'd love to hear them. –  m0r7if3r Jan 12 '12 at 22:38

You can simply add a wp_nonce check before saving the post http://codex.wordpress.org/Function_Reference/wp_nonce_field Once is saved the first time , the wp_nonce will be disgregated and no more calls to wp_insert_post will be done

share|improve this answer
    
I'm already doing an nonce at the //data authenticity check, thanks for answering though –  m0r7if3r Jan 7 '12 at 23:51

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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