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.