Skip to content

Taxonomy: Introduce allow_multiple_terms for single-term taxonomies. - #14352

Open
abhishekfdd wants to merge 1 commit into
WordPress:trunkfrom
abhishekfdd:64991-allow-multiple-terms
Open

abhishekfdd wants to merge 1 commit into
WordPress:trunkfrom
abhishekfdd:64991-allow-multiple-terms

Conversation

@abhishekfdd

Copy link
Copy Markdown

Adds an allow_multiple_terms argument to register_taxonomy() (default true). Setting it to false gives a taxonomy single-term behavior, similar to post formats.

When allow_multiple_terms is false:

  • Meta box: the default meta box becomes post_single_term_meta_box(). It renders radio buttons for hierarchical taxonomies and a <select> for non-hierarchical ones. A "None" option appears unless a default_term is set. A custom meta_box_cb still wins.
  • Sanitizing: taxonomy_meta_box_sanitize_cb_single_term() reduces the submitted value to at most one term ID.
  • Enforcement: wp_set_object_terms() keeps only the last term and treats $append as a replace. REST, WP-CLI and direct callers all follow the rule, not just the admin UI.
  • Quick Edit: renders the same single-select controls. get_inline_data() now outputs a post_single_term element with the current term ID, and inline-edit-post.js uses it to preselect the radio or dropdown.

What changed since 64991.diff

@umarkulachi reported that Quick Edit didn't preselect the saved term. Testing the first patch showed the problem was wider:

  1. Nothing was preselected even on a fresh page load, for both radios and the dropdown. get_inline_data() only exposed term IDs in a form the JS matched against :checkbox, and exposed term names for flat taxonomies, but the dropdown is keyed by ID.
  2. That meant data loss: opening Quick Edit and changing only the title removed the post's term, because the form submitted the empty sentinel.

Both are fixed here, and the patch now includes unit tests (the first patch had none).

Testing

Test plugin (term names deliberately don't match post statuses):

add_action( 'init', function () {
	register_taxonomy( 'book_status', 'post', array( 'label' => 'Book Status', 'hierarchical' => true,  'show_admin_column' => true, 'allow_multiple_terms' => false ) );
	register_taxonomy( 'book_shelf',  'post', array( 'label' => 'Book Shelf',  'hierarchical' => false, 'show_admin_column' => true, 'allow_multiple_terms' => false ) );
} );
add_filter( 'use_block_editor_for_post', '__return_false' );

Quick Edit round trip on Posts > All Posts (automated in Playwright, then checked against the DB with WP-CLI):

Step Before After
Open Quick Edit on fresh load (Hardcover / History saved) nothing selected / "None" Hardcover / History
Save Paperback / Science, reopen without reload nothing selected / "None" Paperback / Science
Change only the title, Update both terms removed terms kept
Choose None, Update, reopen n/a None

Regression checks: Quick Edit for core Categories (checkboxes) and Tags (textarea) still preselects correctly, with no JS errors. The classic meta box restores the saved term.

Unit tests: tests/phpunit/tests/taxonomy/allowMultipleTerms.php (15 tests). It covers default wiring, the custom meta_box_cb precedence, the wp_set_object_terms() cap and the $append downgrade, the sanitize callback, and the get_inline_data() output. The get_inline_data() tests fail without the fix. --group taxonomy passes.

Not in this PR (open questions on the ticket)

  • Block editor sidebar (lives in Gutenberg; needs allow_multiple_terms in the REST taxonomies schema first).
  • REST schema exposure of allow_multiple_terms.
  • Bulk Edit has no per-taxonomy controls for these taxonomies. The server-side cap still applies.

Trac ticket: https://core.trac.wordpress.org/ticket/64991

Use of AI Tools

AI assistance: Yes
Tool(s): Claude
Model(s): Claude Opus 5.5
Used for: Reproducing the Quick Edit bug in a scripted browser test, drafting the get_inline_data() / inline-edit-post.js fix and the unit tests, and drafting this description.

This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

Adds an `allow_multiple_terms` argument to `register_taxonomy()`, defaulting
to `true`. When `false`:

- The default meta box renders radio buttons (hierarchical) or a dropdown
  (non-hierarchical) via the new `post_single_term_meta_box()`.
- Submitted values are sanitized to at most one term ID by the new
  `taxonomy_meta_box_sanitize_cb_single_term()`.
- `wp_set_object_terms()` keeps only the last term and treats `$append`
  as a replace, so REST, WP-CLI and direct callers honor the constraint.
- Quick Edit renders the matching single-select controls, and
  `get_inline_data()` exposes the current term ID so
  `inline-edit-post.js` can preselect it. Without this, Quick Edit opened
  with nothing selected and saving the row (even to change only the title)
  removed the post's term.

Props bastho, umarkulachi.
See #64991.
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props abhishekfdd, rejaulalomkhan.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@rejaulalomkhan

Copy link
Copy Markdown
Member

Thanks for this PR @abhishekfdd

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.

2 participants