Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tax Query: Allow querying for all posts with any term of a given taxonomy #7271

Draft
wants to merge 27 commits into
base: trunk
Choose a base branch
from

Conversation

ockham
Copy link
Contributor

@ockham ockham commented Aug 29, 2024

Diff best viewed with whitespace changes hidden.

What

Introduce a new register_taxonomy() flag (tentatively called root_taxonomy_archive) which, for a given taxonomy — e.g. projects — will enable the "root" route (/projects) (so it no longer is interpreted as a page that doesn’t exists and thus 404s).

Note that this does not extend the template hierarchy to include a dedicated template for root taxonomy archives; that is a different problem that I’d like to tackle separately. For now, root taxonomy routes fall back to the generic archive template.

How

To solve this problem, we need some understanding of WordPress' routing. Internally, there are two important concepts:

  • To represent route parameters, WordPress uses the concept of so-called rewrite tags (no relation to post tags). They are denoted by a leading and trailing percent sign, e.g. %year% or %postname%, and defined via add_rewrite_tag(), where the matching regular expression is provided as an argument; e.g. for %year% – a four-digit number – the RegEx is ([0-9]{4}).
  • These rewrite tags are then used to compose the higher-level permalink structure ("permastruct"), which defines the list of all permissible route patterns. For single post routes, these permalink structures can be customized via Settings > Permalinks. When a visitor then browses a URL of the site, WordPress matches the route against those patterns. It then goes through all rewrite tags one by one, and translates them individually to key=value pairs, from which it assembles a query string -- which is the canonical representation for any route! E.g. /2024/12/hello-world is matched by /%year%/%month%/%postname%, which is then translated into /?year=2024&month=12&name=hello-world.

For a taxonomy called projects, the permastruct is /projects/%project%. Then, a route such as /projects/film is mapped to the query string ?projects=film, as can be easily verified using the Query Monitor plugin.

134VYPNmm0pJ7njR2L8-ySRCOCfdzgNs2eg18Szd2

The crux here is WordPress' rigid mapping of any rewrite tag to a key/value pair, used in a query string (like ?key=value). Thus, the first part of the solution found in this PR is to allow a "dangling" query string, i.e. ?projects= (with no value assigned). Note that this is not in violation of the URI RFC!

The second part is to define a permastruct that matches a route like /projects and translates it to that "dangling" ?projects= query string. The tricky part here is that WordPress really, really wants us to use a rewrite tag that it will match against the route, and use the value it finds as the value in the query string. We work around this by adding a RegEx capturing group that will match the empty string: ().

Testing instructions

  1. Add the following code (e.g. to your theme's functions.php). Create a number of new posts, and use the "Project Types" panel in the block inspector to create a number of "Project Type" terms and assign them to each given post. Use terms such as "Film" or "TV".
Code
function register_taxonomies() {
	/**
	 * Taxonomy: Project Types.
	 */
	 $labels = array(
		'name'          => __( 'Project Types' ),
		'singular_name' => __( 'Project Type' ),
		'add_new_item'  => __( 'Add Project Type' ),
		'new_item_name' => __( 'New Project Type' ),
	);

	$args = array(
		'label'                 => __( 'Project Types' ),
		'labels'                => $labels,
		'show_in_rest'          => true,
		'show_tagcloud'         => false,
	);
	register_taxonomy( 'projects', array( 'post' ), $args );
}
add_action( 'init', 'register_taxonomies', 0 );
  1. Go to Settings > Permalinks, and click the "Save changes" button at the bottom. (This is required for WordPress to update its internal routing to include routes for the newly added taxonomy.)

  2. Manually navigate to /projects/film and verify that it still works, displaying an archive of posts that have the "Film" term assigned.

  3. Manually navigate to /projects and verify that it also works: It should show an archive of all posts that have at least one term from the projects taxonomy assigned. (It will currently use the generic archive template. Adding a template for the root taxonomy route will be handled in a follow-up.)

(You can compare this behavior to trunk, where /projects will 404.)

Finally, also try the query strings ?projects=film and ?projects=. They should also display the correct archives.

Follow-up work

In #7989, I've started exploring extending the permalink structure to include a dedicated template for the root taxonomy route.

References

Some relevant documentation here.

Screencast

taxonomy-root-archives

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


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.

@ockham ockham self-assigned this Aug 29, 2024
Copy link

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

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • 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.

@ockham
Copy link
Contributor Author

ockham commented Aug 29, 2024

Noting here that if we don't want to implement this kind of behavior universally for all taxonomies, we could add an argument to register_taxonomy to control it.

@ockham ockham force-pushed the add/tax-query-for-all-terms-in-taxonomy branch 2 times, most recently from 7baa25c to d3b5c55 Compare September 11, 2024 10:28
@ockham ockham force-pushed the add/tax-query-for-all-terms-in-taxonomy branch from ca67943 to 38d1d42 Compare October 9, 2024 09:35
add_rewrite_tag( "%$this->name%", $tag, $this->query_var ? "{$this->query_var}=" : "taxonomy=$this->name&term=" );
$query = $this->query_var ? "{$this->query_var}=" : "taxonomy=$this->name&term=";

add_rewrite_tag( "%taxonomy-$this->name%", "$this->name()", $query );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The trailing () here is a RegEx capturing group. WordPress really wants to retrieve a match from this RegEx that it'll use as the value in the query string; it'll automatically build a query string like this: ?key=$matches[0], using the $matches var resulting from applying the RegEx.

Since we don't want to set any value, we work around this by providing a capturing group that will return the empty string, thus resulting in ?key=.

@gziolo
Copy link
Member

gziolo commented Nov 19, 2024

To allow using just one template to render both that root taxonomy route (/projects), as well as the taxonomy archives for each of the terms in that taxonomy (e.g. /projects/film, /projects/tv, etc).

The recent proposal from @youknowriad about architectural changes to how templates get linked with themes might resolve this issue because we would finally be able to have 1 template file applicable in multiple scenarios:

@ockham ockham force-pushed the add/tax-query-for-all-terms-in-taxonomy branch 2 times, most recently from 66a05a8 to 91ddfda Compare December 5, 2024 13:53
@ockham
Copy link
Contributor Author

ockham commented Dec 5, 2024

To allow using just one template to render both that root taxonomy route (/projects), as well as the taxonomy archives for each of the terms in that taxonomy (e.g. /projects/film, /projects/tv, etc).

The recent proposal from @youknowriad about architectural changes to how templates get linked with themes might resolve this issue because we would finally be able to have 1 template file applicable in multiple scenarios:

AFAIU that proposal would allow the user to assign custom templates to any template "entity" in the template hierarchy (i.e. to any red, green, or blue box in this diagram). This means that it'd be still somewhat coupled to the template hierarchy, as opposed to allowing assigning a custom template to any given route.

As a consequence, I think we'd still need to extend the template hierarchy to contain a template "entity" that's shared between existing taxonomy archive template entities (i.e. taxonomy-$taxonomy-$term, taxonomy-$taxonomy, and taxonomy), and the root taxonomy route we're seeking to implement in this PR 🙂

@ockham
Copy link
Contributor Author

ockham commented Dec 5, 2024

BTW as discussed on our call the other day, extending the template hierarchy isn't particularly complex, but very cumbersome, and there are a few different ways to do that. I started implementing that as part of this PR but have now removed the relevant commits. I'd rather tackle that in a separate PR so that this one can solely focus on enabling the root taxonomy routes, without involving the template hierarchy yet.

I'll update the PR description accordingly.

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