[wp-trac] [WordPress Trac] #64991: New param "allow_multiple_terms" in `register_taxonomy` for single term restriction
WordPress Trac
noreply at wordpress.org
Fri Apr 24 18:12:04 UTC 2026
#64991: New param "allow_multiple_terms" in `register_taxonomy` for single term
restriction
-------------------------------------------------+-------------------------
Reporter: bastho | Owner: (none)
Type: feature request | Status: new
Priority: normal | Milestone: Awaiting
| Review
Component: Taxonomy | Version:
Severity: normal | Resolution:
Keywords: has-patch has-unit-tests needs- | Focuses: ui
testing |
-------------------------------------------------+-------------------------
Changes (by abhishekfdd):
* keywords: => has-patch has-unit-tests needs-testing
Comment:
Thanks for opening this, @bastho. Attaching a first patch (64991.diff)
that covers the feature end-to-end, along with the reasoning behind a few
decisions that came up while writing it. Happy to iterate on any of them.
What the patch changes
WP_Taxonomy — adds an $allow_multiple_terms property (default true) and
accepts it in set_props(). When false and no custom meta_box_cb is
supplied, the default meta box callback is switched to a new
post_single_term_meta_box() and the sanitize callback to a new
taxonomy_meta_box_sanitize_cb_single_term().
register_taxonomy() docblock — documents the new argument and a new @since
7.1.0 tag for the arg introduction.
post_single_term_meta_box() (new, in wp-admin/includes/meta-boxes.php) —
renders radio buttons for hierarchical taxonomies and a <select> (via
wp_dropdown_categories()) for non-hierarchical ones. A "None" option is
offered when no default_term is configured.
taxonomy_meta_box_sanitize_cb_single_term() (new, in wp-
admin/includes/post.php) — normalizes the submitted array to at most one
term ID.
wp_set_object_terms() — enforces the cap at the shared assignment path, so
REST, WP-CLI, and any direct caller honor the constraint — not just the
admin UI. When $append is passed for a single-term taxonomy, the append is
downgraded to a replace to avoid quietly stacking terms.
Quick Edit (WP_Posts_List_Table) — for hierarchical taxonomies, renders a
radio list instead of wp_terms_checklist(); for non-hierarchical, renders
a dropdown instead of the tags <textarea>. Bulk Edit already excludes per-
taxonomy controls for non-category taxonomies, so the server-side cap in
(5) is what protects bulk operations.
Design decisions worth flagging
Argument name: allow_multiple_terms. I kept the name you proposed.
single_value reads cleanly as a property but pairs awkwardly with a
default of true ('single_value' => true to mean "allow many" is a double
negative). A meta_box_style enum was tempting for extensibility, but it
conflates UI shape with cardinality — the server-side cap has to happen
regardless of which UI is used, so a boolean flag that governs both
defaults matches the semantics better.
Default is true. Every existing register_taxonomy() call is unaffected.
Enforcement lives in wp_set_object_terms(), not just the metabox
sanitizer. This was the biggest design call. The alternative — only
capping in the sanitize callback — would leave REST (POST /wp/v2/posts
with a tax array of many IDs), WP-CLI, and any plugin calling
wp_set_post_terms() free to bypass the constraint. Centralizing it at the
assignment path matches how default_term and other taxonomy-level
semantics are enforced, and keeps the contract consistent for API
consumers.
"Last value wins" when multiple terms are submitted. This matches post-
format semantics and means that if a form somehow submits two radio values
(e.g. a custom client), the most recent one wins. Open to a stricter
WP_Error instead if that's preferred.
Custom meta_box_cb precedence. If a caller supplies their own meta_box_cb,
we don't override it even when allow_multiple_terms => false — the custom
UI is assumed to know what it's doing. The server-side cap still applies,
so a misbehaving custom UI can't save multiple terms.
Gutenberg / block editor. Out of scope for Core Trac — belongs in the
gutenberg repo. The useEntityProp-backed PostTaxonomies component would
need to read allow_multiple_terms (ideally exposed via the taxonomies REST
schema) and render a <RadioControl> / <SelectControl> in place of the
flat/hierarchical token field. Worth a companion ticket once the Core API
shape is settled here. Also not yet exposed in the REST taxonomies
endpoint — I didn't add it to
WP_REST_Taxonomies_Controller::get_item_schema() in this patch because the
API surface felt worth agreeing on first, but it's a natural follow-up.
Bulk Edit. The existing UI already suppresses per-taxonomy controls in
bulk mode (! $bulk), and the cap in wp_set_object_terms() means bulk-
updating many posts to a single term still works correctly. No per-row
bulk UI change needed, though an explicit bulk-edit dropdown for single-
term taxonomies could be a follow-up enhancement.
Open questions
Argument naming — fine as-is, or would committers prefer an alternative?
REST schema exposure — add allow_multiple_terms to the taxonomies endpoint
schema in this patch or a follow-up?
default_term interaction — when allow_multiple_terms => false and a
default_term is set, I'm suppressing the "None" option so the UI always
reflects a valid selection. Happy to make that configurable if there's a
use case for allowing explicit clearing.
Unit tests — haven't added them in this first pass; will follow up in a
separate patch once the API shape is agreed, focused on
wp_set_object_terms() cap behavior (including the $append downgrade), the
new sanitize callback, and register_taxonomy() defaults wiring.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/64991#comment:1>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list