diff --git a/includes/sync.php b/includes/sync.php index 57d07fb..b84f699 100644 --- a/includes/sync.php +++ b/includes/sync.php @@ -87,6 +87,27 @@ function sync_shadow_taxonomies( int $post_id, \WP_Post $post_after, bool $updat return; } + // If a post is already published and not undergoing a status change, but does not + // have an associated term, create one. + if ( 'publish' === $status_before && 'publish' === $status_after && false === $term_after ) { + // In the very unlikely condition that a term _was_ associated, but now is + // lost to the ether, attempt to restore previous post associations. + $existing_associations = (array) get_post_meta( $post_id, "{$taxonomy}_associated_posts", true ); + $existing_associations = array_filter( $existing_associations ); + + $new_term = wp_insert_term( $title_after, $taxonomy ); + + if ( is_wp_error( $new_term ) ) { + return; + } + + foreach ( $existing_associations as $association ) { + wp_set_object_terms( $association, $new_term['term_id'], $taxonomy ); + } + + return; + } + // If the post transitioned from published to not published, remove the associated term. if ( 'publish' !== $status_after && $term_before ) { $associated_posts = get_objects_in_term( $term_before->term_id, $taxonomy ); diff --git a/tests/test-term-sync.php b/tests/test-term-sync.php index 08ba63a..14194f7 100644 --- a/tests/test-term-sync.php +++ b/tests/test-term-sync.php @@ -539,6 +539,44 @@ public function test_post_trash_to_publish_creates_term_and_restores_relationshi $this->assertEquals( array( $term->slug ), $associated_terms, 'Existing shadow term relationships should be restored when its connected post is moved from trash to publish.' ); } + /** + * A published post without a shadow term should have one created on update. + */ + public function test_post_publish_to_publish_creates_missing_term(): void { + $post = $this->factory()->post->create( + array( + 'post_type' => 'example', + 'post_title' => 'Hazelnut', + 'post_status' => 'publish', + ) + ); + + if ( is_wp_error( $post ) ) { + $this->fail( 'Failed to create post.' ); + } + + $post = get_post( $post ); + $term = get_term_by( 'slug', 'hazelnut', 'example_connect', 'OBJECT' ); + + if ( ! $term ) { + $this->fail( 'Expected term not available.' ); + } + + // Delete the shadow term to simulate the missing term condition. + wp_delete_term( $term->term_id, 'example_connect' ); + + $term = get_term_by( 'slug', 'hazelnut', 'example_connect', 'OBJECT' ); + $this->assertFalse( $term, 'Shadow term should have been deleted.' ); + + // Update the post without changing its status. + wp_update_post( $post ); + + $term = get_term_by( 'name', 'Hazelnut', 'example_connect', 'OBJECT' ); + $term_name = ! $term ? '' : $term->name; + + $this->assertEquals( 'Hazelnut', $term_name, 'A published post without a shadow term should have one created on update.' ); + } + /** * An existing published post that has its title changed should change the * title of its shadow term.