Skip to content

[MOOSE-377] Update related posts selection and taxonomy matching#345

Open
LayaTaal wants to merge 2 commits intomainfrom
feature/MOOSE-377/update-related-posts-selection
Open

[MOOSE-377] Update related posts selection and taxonomy matching#345
LayaTaal wants to merge 2 commits intomainfrom
feature/MOOSE-377/update-related-posts-selection

Conversation

@LayaTaal
Copy link
Copy Markdown
Contributor

@LayaTaal LayaTaal commented Apr 8, 2026

What does this do/fix?

Ports the related posts updates from moderntribe/nexcess-2026 into ModernPress.

  • Automatic related post queries now use the current post type instead of only post
  • Automatic mode can relate by supported taxonomies or show latest items, and falls back to latest same-type content when taxonomy matches are unavailable (screenshot)
  • Manual mode can select mixed post types in the editor (screenshot)
  • Related post cards now display the active taxonomy label only when taxonomy-based matching is in effect (screenshot)

QA

Links to relevant issues

Screenshots/video:

Automatic Selection

Pull request checklist

  • I've added a changelog entry for these changes.
  • I've linked to a relevant Jira issue.
  • I've captured a screenshot or screencast of the changes and linked it above.

LayaTaal added 2 commits April 8, 2026 13:22
Support current post types, taxonomy-based automatic matching, and mixed-type manual selection so the related posts block works across more starter use cases.
Address the controller formatting issues caught by the push hooks so the related posts backport can pass repository checks.
@LayaTaal LayaTaal self-assigned this Apr 8, 2026
@LayaTaal LayaTaal marked this pull request as draft April 8, 2026 18:24
@LayaTaal LayaTaal marked this pull request as ready for review April 9, 2026 01:46
@LayaTaal LayaTaal requested review from a team and Copilot April 9, 2026 01:46
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the Related Posts block to support current post types, taxonomy-based matching with fallbacks, mixed post types for manual selection, and conditional display of taxonomy labels on related post cards.

Changes:

  • Extend automatic related-post queries to use the current post type and optionally relate by a selected taxonomy (with fallback to latest items).
  • Allow manual selection of related items across multiple post types in the block editor.
  • Only display a taxonomy/term label on related post cards when taxonomy-based matching is actually active.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
wp-content/themes/core/components/cards/post.php Pass taxonomy context into post cards and switch label rendering to the new “display term” API.
wp-content/themes/core/blocks/tribe/related-posts/render.php Provide taxonomy context from the controller to the card template.
wp-content/themes/core/blocks/tribe/related-posts/edit.js Add taxonomy “Related By” control; expand manual picker to fetch/select across post types; adjust SSR attributes.
wp-content/themes/core/blocks/tribe/related-posts/block.json Add taxonomySlug attribute to persist the automatic matching mode.
wp-content/plugins/core/src/Components/Traits/Post_Data.php Add display-term support to control whether/which taxonomy term label appears on cards.
wp-content/plugins/core/src/Components/Blocks/Related_Posts_Controller.php Update query logic for current post type, taxonomy matching, fallbacks, and expose card taxonomy context.
wp-content/plugins/core/src/Components/Blocks/Post_Card_Controller.php Wire taxonomy slug into card controller for display-term selection.
CHANGELOG.md Add changelog entry for related posts updates.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +69 to +71
'post_type' => 'any',
'post__in' => array_map( 'absint', wp_list_pluck( $this->chosen_posts, 'id' ) ),
'orderby' => 'post__in',
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manual selection queries don't set posts_per_page, so WordPress will apply the default posts-per-page limit (often 10) and can truncate the selected post__in list. Consider setting posts_per_page to count( $this->chosen_posts ) (or at least a large enough value) when post__in is used so all manually chosen posts render.

Suggested change
'post_type' => 'any',
'post__in' => array_map( 'absint', wp_list_pluck( $this->chosen_posts, 'id' ) ),
'orderby' => 'post__in',
'post_type' => 'any',
'post__in' => array_map( 'absint', wp_list_pluck( $this->chosen_posts, 'id' ) ),
'orderby' => 'post__in',
'posts_per_page' => count( $this->chosen_posts ),

Copilot uses AI. Check for mistakes.
return 'category';
}

return taxonomies[ 0 ]?.slug ?? '';
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_effective_taxonomy_slug can return an empty string when the current post type has no REST-exposed taxonomies. In the editor this leads to a SelectControl value that doesn't match any option (options always include Latest Items). Consider falling back to the __latest__ value when taxonomies.length === 0 so UI and SSR behavior stay consistent.

Suggested change
return taxonomies[ 0 ]?.slug ?? '';
return taxonomies[ 0 ]?.slug ?? LATEST_ITEMS_VALUE;

Copilot uses AI. Check for mistakes.

const postList = selectableTypes.flatMap( ( type ) => {
const records = getEntityRecords( 'postType', type.slug, {
per_page: 100,
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The editor builds postList by calling getEntityRecords once per selectable post type (flatMap over types). On sites with many post types this can trigger a burst of REST requests and load a lot of post data into the editor. Consider narrowing the queried types, lowering per_page, or switching to an on-demand/search-based fetch (e.g., fetch records only when the user types) to avoid N+1 requests.

Suggested change
per_page: 100,
per_page: 20,

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +89
$terms = get_the_terms( $this->post_id, $taxonomy );
if ( $terms && ! is_wp_error( $terms ) ) {
$this->display_term = reset( $terms ) ?: null;

return;
}

$this->display_term = null;
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For non-category taxonomies, set_display_term_taxonomy() uses reset( $terms ) rather than the existing get_primary_term() helper (which already supports arbitrary taxonomies and respects Yoast/RankMath primary term settings). Consider using get_primary_term( $this->post_id, $taxonomy ) here to keep displayed terms consistent with the project's primary-term logic.

Suggested change
$terms = get_the_terms( $this->post_id, $taxonomy );
if ( $terms && ! is_wp_error( $terms ) ) {
$this->display_term = reset( $terms ) ?: null;
return;
}
$this->display_term = null;
$this->display_term = $this->get_primary_term( $this->post_id, $taxonomy );

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to this. get_primary_term already falls back to exactly what you have here, it's possible you don't need this entire function?

Copy link
Copy Markdown
Contributor

@MlKilderkin MlKilderkin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢


$terms = get_the_terms( $this->post_id, $taxonomy );
if ( $terms && ! is_wp_error( $terms ) ) {
$this->display_term = reset( $terms ) ?: null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add some context here? How we can have reset( $terms ) empty?
get_the_terms returns WP_Error, false, array of WP_Terms. If we pass if statement we will have $terms that has at least 1 WP_Term element.

*/

$post_id = $args['post_id'];
$post_id = $args['post_id'];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious why do we need pass post_id here? get_the_ID() provides incorrect value?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can add a fallback e,g if post_id is empty use get_the_ID()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MlKilderkin post_id is here for a couple of reasons, but you're right in that it doesn't always have to be.

  1. It's passed from the Related Posts render template right above (this is the area where it probably doesn't need to be passed)
  2. It gets generated in the editor in a weird way due to how the Query block works and MUST be passed to the card component: https://github.com/moderntribe/ModernPress/blob/a4c28941215760c98541808bb8869849862aa920/wp-content/themes/core/blocks/tribe/post-card/render.php

I would say in general, in places where we're calling the component in a loop we've defined we can probably just default to get_the_ID() but when we're using the component in the editor as part of the Query block, we need to be passing the ID as get_the_ID() doesn't exist in that context.

Copy link
Copy Markdown
Contributor

@GeoffDusome GeoffDusome left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 These changes look good to me, but I'd like to get a look from @Vinsanity or @dpellenwood (since I know as soon as I submit this review everyone else will lose the ability to) if we want to go this direction or if it would make things less confusing / complex to just be duplicating the original block.

My original intention with this block was to keep it simple, yet configurable by a dev. Keeping it to one post type meant that the admin experience was obvious and not as complex. I had intended the block to be duplicated and switched to other post types (and other query params) by a dev, if necessary, as other post types may also have a different card view (not that you can't switch/case the post type to change the card with this update).

The post.php template part, was specifically made for the Posts post type, I would assume something like an Events post type would not only include a different card element, but would also require edits/additions to the WP_Query of the block.

All this being said, I do like the "Related By" option addition and I think this part of the PR could stay regardless of where we decide to go.

*/

$post_id = $args['post_id'];
$post_id = $args['post_id'];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MlKilderkin post_id is here for a couple of reasons, but you're right in that it doesn't always have to be.

  1. It's passed from the Related Posts render template right above (this is the area where it probably doesn't need to be passed)
  2. It gets generated in the editor in a weird way due to how the Query block works and MUST be passed to the card component: https://github.com/moderntribe/ModernPress/blob/a4c28941215760c98541808bb8869849862aa920/wp-content/themes/core/blocks/tribe/post-card/render.php

I would say in general, in places where we're calling the component in a loop we've defined we can probably just default to get_the_ID() but when we're using the component in the editor as part of the Query block, we need to be passing the ID as get_the_ID() doesn't exist in that context.

Comment on lines +82 to +89
$terms = get_the_terms( $this->post_id, $taxonomy );
if ( $terms && ! is_wp_error( $terms ) ) {
$this->display_term = reset( $terms ) ?: null;

return;
}

$this->display_term = null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to this. get_primary_term already falls back to exactly what you have here, it's possible you don't need this entire function?

@LayaTaal
Copy link
Copy Markdown
Contributor Author

🟢 These changes look good to me, but I'd like to get a look from @Vinsanity or @dpellenwood (since I know as soon as I submit this review everyone else will lose the ability to) if we want to go this direction or if it would make things less confusing / complex to just be duplicating the original block.

My original intention with this block was to keep it simple, yet configurable by a dev. Keeping it to one post type meant that the admin experience was obvious and not as complex. I had intended the block to be duplicated and switched to other post types (and other query params) by a dev, if necessary, as other post types may also have a different card view (not that you can't switch/case the post type to change the card with this update).

The post.php template part, was specifically made for the Posts post type, I would assume something like an Events post type would not only include a different card element, but would also require edits/additions to the WP_Query of the block.

All this being said, I do like the "Related By" option addition and I think this part of the PR could stay regardless of where we decide to go.

This is a great callout and I'd also like to hear some more opinions on the direction we want to go with this. I can see an argument for both ways.

@dpellenwood
Copy link
Copy Markdown
Collaborator

The post.php template part, was specifically made for the Posts post type, I would assume something like an Events post type would not only include a different card element, but would also require edits/additions to the WP_Query of the block.

All this being said, I do like the "Related By" option addition and I think this part of the PR could stay regardless of where we decide to go.

Thank you both for the thoughtful dialog here. I, too, can see the rational for both directions. I'm wondering if we can sort of split the difference? I do like the flexibility of a generic query block (that renders a generic card regardless of the post data returned) where an editor can create more complex CPT & taxonomy queries. I could see this existing in the generic sense and still implementing separate blocks for specific post types (or card styles) where we need to, say, get a query of People or Events that don't use traditional query values or display styles. Thoughts?

@GeoffDusome
Copy link
Copy Markdown
Contributor

I could see this existing in the generic sense and still implementing separate blocks for specific post types (or card styles) where we need to, say, get a query of People or Events that don't use traditional query values or display styles. Thoughts?

The only issue I have with this is the language. "Posts" is what we call generic posts in WP, but it's also the name of a post type. If we're adding "Related Events", "Related Posts" to me (if I'm a client editor) means that it's specifically related to the Posts post type (even though we know it means more). If we can figure out a way to adjust language, even if it's description or helper text based I'm fine with this approach.

@LayaTaal
Copy link
Copy Markdown
Contributor Author

@dpellenwood @GeoffDusome I'm going to take a pass back over this to see if I can find a good compromise spot for our two paths and I'll update here.

@LayaTaal
Copy link
Copy Markdown
Contributor Author

@dpellenwood @GeoffDusome I thought about this some more and wanted to share. Please let me know if you have any push back. Once we are aligned I will go through the actual code change requests to get this wrapped up or closed.

Does this improve the experience or over complicate it?

I think having a generic catch all query and card template is more helpful than it is harmful. It gives devs and users an already built way to handle most cases without having to create new code.

For post types that require something unique, it is easy to create a new template and conditional to handle that case. We also have options to restrict where this general block is available or further customize it if it makes sense.

I also think the way this is currently setup is a pretty intuitive experience for users - choosing automatic or manual, and then deciding the relation to use or selecting specific posts which are identified by post type.

Block naming

I don't have as much of a concern as @GeoffDusome about continuing to use the word "posts". I think the semantics of that are more important to developers than content creators. That being said, I think an alternative block name could be Related Content to make it more clear and generic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants