[MOOSE-377] Update related posts selection and taxonomy matching#345
[MOOSE-377] Update related posts selection and taxonomy matching#345
Conversation
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.
There was a problem hiding this comment.
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.
| 'post_type' => 'any', | ||
| 'post__in' => array_map( 'absint', wp_list_pluck( $this->chosen_posts, 'id' ) ), | ||
| 'orderby' => 'post__in', |
There was a problem hiding this comment.
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.
| '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 ), |
| return 'category'; | ||
| } | ||
|
|
||
| return taxonomies[ 0 ]?.slug ?? ''; |
There was a problem hiding this comment.
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.
| return taxonomies[ 0 ]?.slug ?? ''; | |
| return taxonomies[ 0 ]?.slug ?? LATEST_ITEMS_VALUE; |
|
|
||
| const postList = selectableTypes.flatMap( ( type ) => { | ||
| const records = getEntityRecords( 'postType', type.slug, { | ||
| per_page: 100, |
There was a problem hiding this comment.
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.
| per_page: 100, | |
| per_page: 20, |
| $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; |
There was a problem hiding this comment.
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.
| $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 ); |
There was a problem hiding this comment.
+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?
|
|
||
| $terms = get_the_terms( $this->post_id, $taxonomy ); | ||
| if ( $terms && ! is_wp_error( $terms ) ) { | ||
| $this->display_term = reset( $terms ) ?: null; |
There was a problem hiding this comment.
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']; |
There was a problem hiding this comment.
curious why do we need pass post_id here? get_the_ID() provides incorrect value?
There was a problem hiding this comment.
maybe we can add a fallback e,g if post_id is empty use get_the_ID()
There was a problem hiding this comment.
@MlKilderkin post_id is here for a couple of reasons, but you're right in that it doesn't always have to be.
- It's passed from the Related Posts render template right above (this is the area where it probably doesn't need to be passed)
- 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.
GeoffDusome
left a comment
There was a problem hiding this comment.
🟢 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']; |
There was a problem hiding this comment.
@MlKilderkin post_id is here for a couple of reasons, but you're right in that it doesn't always have to be.
- It's passed from the Related Posts render template right above (this is the area where it probably doesn't need to be passed)
- 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.
| $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; |
There was a problem hiding this comment.
+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?
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. |
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? |
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. |
|
@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. |
|
@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 namingI 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 |
What does this do/fix?
Ports the related posts updates from
moderntribe/nexcess-2026into ModernPress.postQA
Links to relevant issues
Screenshots/video:
Automatic Selection
Pull request checklist