How to merge two or more WordPress posts?

The requirements you are describing require much more of an explanation than is really suitable here. A simple solution might be to mark the duplicate posts as duplicates via a meta box on the post screen. This meta box would have one input: A reference to another post.

Check out this tutorial on how to build a custom meta box.

Suppose you’ve set up a meta box that will allow the user to mark a post that is a duplicate of another post. Say the duplicate post is marked with a meta key _duplicate_of_post, whose meta value is the canonical post’s post ID.

You can then on filter the_posts filter to pull together any duplicates, folding the the canonical post together with any of its duplicates:

function my_the_posts_filter( $posts, $query ) {
    // Only operate on the main query.
    if( !$query->is_main_query() )
         return $posts;

    // Store canonical posts here. 
    $canonicals = array();

    // Store the duplicates here, keyed by canonical post ID
    $duplicates = array();

    foreach( $posts as $post ) {
        if( $dupe_id = get_post_meta( $post->ID, '_duplicate_of_post', true ) ) {
           $canonicals[] = get_post( $dupe_id ); 

           $duplicates[$dupe_id] = get_posts( array( 'meta_key' => '_duplicate_of_post', 'meta_value' => $dupe_id, 'posts_per_page' => -1, 'post__not_in' => $post->ID ) );
        }
        else {
           $canonicals[] = $post;

           // Get all posts who have this marked as duplicate
           $duplicates[$post->ID] = get_posts( array( 'meta_key' => '_duplicate_of_post', 'meta_value' => $post->ID, 'posts_per_page' => -1 ) );
        }
    }

    // Append all of the other post content to the canonicals separated
    // by two newlines
    foreach( $canonicals as $post )
       $post->post_content .= array_join( "\n\n", wp_list_pluck( $duplicates[$dupe_id], 'post_content' ) );

    return $canonicals;
}
add_filter( 'the_posts', 'my_the_posts_filter', 10, 2 );

One caveat in implementation of the post reference meta box is that you’d have to implement the logic to ensure that you can’t set a post as a duplicate post is if it’s already marked as a “master” post by another post. Without that restriction, the example above will break in unexpected ways.

Update

You can also hook on save_post, combine, then trash posts marked as duplicates:

function my_save_post( $post ) {
    // Don't do on autosave
    if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;

    if( isset( $_POST['_duplicate_of_post'] ) && ( $dupe_of_id = (int) $_POST['_duplicate_of_post'] ) && ( $source = get_post( $dupe_of_id ) ) ) {  
       // Add this post's content to the canonical version               
       $update = array( 'ID' => $source->ID, 'post_content' => $source->post_content );
       $update['post_content'] .= "\n\n" . $post->post_content;

       // Update canonical
       wp_update_post( $update );

       // Trash this post
       wp_trash_post( $post->ID );
    }
}
add_action( 'save_post', 'my_save_post' );

This is the most basic of examples. It’s expecting a form field on the edit post screen named _duplicate_of_post which contains the canonical post’s ID. Note that I’m using ONLY the WordPress API, which is something you should always aspire to do while developing theme and plugin code.

Leave a Comment