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
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions src/wp-includes/class-wp-query.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -1169,7 +1169,16 @@ public function parse_tax_query( &$q ) {
continue; // Handled further down in the $q['tag'] block. continue; // Handled further down in the $q['tag'] block.
} }


if ( $t->query_var && ! empty( $q[ $t->query_var ] ) ) { if ( ! $t->query_var || ! isset( $q[ $t->query_var ] ) ) {
continue;
}

if ( empty( $q[ $t->query_var ] ) && ! $t->root_taxonomy_archive ) {
// We only allow the term to be empty if the taxonomy is set up to
// show its root archive.
continue;
}

$tax_query_defaults = array( $tax_query_defaults = array(
'taxonomy' => $taxonomy, 'taxonomy' => $taxonomy,
'field' => 'slug', 'field' => 'slug',
Expand All @@ -1195,14 +1204,20 @@ public function parse_tax_query( &$q ) {
) )
); );
} }
} else { } elseif ( ! empty( $term ) ) {
$tax_query[] = array_merge( $tax_query[] = array_merge(
$tax_query_defaults, $tax_query_defaults,
array( array(
'terms' => preg_split( '/[,]+/', $term ), 'terms' => preg_split( '/[,]+/', $term ),
) )
); );
} } else {
$tax_query[] = array_merge(
$tax_query_defaults,
array(
'operator' => 'EXISTS',
)
);
} }
} }


Expand Down
20 changes: 19 additions & 1 deletion src/wp-includes/class-wp-taxonomy.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -245,6 +245,15 @@ final class WP_Taxonomy {
*/ */
public $default_term; public $default_term;


/**
* Whether to show a page for the root taxonomy route, displaying all
* posts that have any term from the taxonomy assigned.
*
* @since 6.8.0
* @var bool
*/
public $root_taxonomy_archive = false;

/** /**
* Whether terms in this taxonomy should be sorted in the order they are provided to `wp_set_object_terms()`. * Whether terms in this taxonomy should be sorted in the order they are provided to `wp_set_object_terms()`.
* *
Expand Down Expand Up @@ -359,6 +368,7 @@ public function set_props( $object_type, $args ) {
'rest_namespace' => false, 'rest_namespace' => false,
'rest_controller_class' => false, 'rest_controller_class' => false,
'default_term' => null, 'default_term' => null,
'root_taxonomy_archive' => false,
'sort' => null, 'sort' => null,
'args' => null, 'args' => null,
'_builtin' => false, '_builtin' => false,
Expand Down Expand Up @@ -510,7 +520,12 @@ public function add_rewrite_rules() {
$tag = '([^/]+)'; $tag = '([^/]+)';
} }


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=.

add_permastruct( "taxonomy-$this->name", "%taxonomy-$this->name%", $this->rewrite );

add_rewrite_tag( "%$this->name%", $tag, $query );
add_permastruct( $this->name, "{$this->rewrite['slug']}/%$this->name%", $this->rewrite ); add_permastruct( $this->name, "{$this->rewrite['slug']}/%$this->name%", $this->rewrite );
} }
} }
Expand All @@ -535,6 +550,9 @@ public function remove_rewrite_rules() {
if ( false !== $this->rewrite ) { if ( false !== $this->rewrite ) {
remove_rewrite_tag( "%$this->name%" ); remove_rewrite_tag( "%$this->name%" );
remove_permastruct( $this->name ); remove_permastruct( $this->name );

remove_rewrite_tag( "%taxonomy-$this->name%" );
remove_permastruct( "taxonomy-$this->name" );
} }
} }


Expand Down
3 changes: 3 additions & 0 deletions src/wp-includes/taxonomy.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ function is_taxonomy_hierarchical( $taxonomy ) {
* @since 5.4.0 Added the registered taxonomy object as a return value. * @since 5.4.0 Added the registered taxonomy object as a return value.
* @since 5.5.0 Introduced `default_term` argument. * @since 5.5.0 Introduced `default_term` argument.
* @since 5.9.0 Introduced `rest_namespace` argument. * @since 5.9.0 Introduced `rest_namespace` argument.
* @since 6.8.0 Introduced `root_taxonomy_archive` argument.
* *
* @global WP_Taxonomy[] $wp_taxonomies Registered taxonomies. * @global WP_Taxonomy[] $wp_taxonomies Registered taxonomies.
* *
Expand Down Expand Up @@ -506,6 +507,8 @@ function is_taxonomy_hierarchical( $taxonomy ) {
* @type string $slug Slug for default term. Default empty. * @type string $slug Slug for default term. Default empty.
* @type string $description Description for default term. Default empty. * @type string $description Description for default term. Default empty.
* } * }
* @type bool $root_taxonomy_archive Whether to show a page at the root taxonomy route, displaying all
* posts that have any term from the taxonomy assigned. Default false.
* @type bool $sort Whether terms in this taxonomy should be sorted in the order they are * @type bool $sort Whether terms in this taxonomy should be sorted in the order they are
* provided to `wp_set_object_terms()`. Default null which equates to false. * provided to `wp_set_object_terms()`. Default null which equates to false.
* @type array $args Array of arguments to automatically use inside `wp_get_object_terms()` * @type array $args Array of arguments to automatically use inside `wp_get_object_terms()`
Expand Down
42 changes: 42 additions & 0 deletions tests/phpunit/tests/canonical.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -87,13 +87,32 @@ public function data_canonical() {
array( '?cat=%d', array( 'url' => '/category/parent/' ), 15256 ), array( '?cat=%d', array( 'url' => '/category/parent/' ), 15256 ),
array( '?cat=%d', array( 'url' => '/category/parent/child-1/' ), 15256 ), array( '?cat=%d', array( 'url' => '/category/parent/child-1/' ), 15256 ),
array( '?cat=%d', array( 'url' => '/category/parent/child-1/child-2/' ) ), // No children. array( '?cat=%d', array( 'url' => '/category/parent/child-1/child-2/' ) ), // No children.
array(
'/category/',
array(
'url' => '/category/',
'qv' => array( 'category_name' => '' ),
),
61957,
),
array( array(
'/category/uncategorized/', '/category/uncategorized/',
array( array(
'url' => '/category/uncategorized/', 'url' => '/category/uncategorized/',
'qv' => array( 'category_name' => 'uncategorized' ), 'qv' => array( 'category_name' => 'uncategorized' ),
), ),
), ),
array(
'/category/page/2/',
array(
'url' => '/category/page/2/',
'qv' => array(
'category_name' => '',
'paged' => 2,
),
),
61957,
),
array( array(
'/category/uncategorized/page/2/', '/category/uncategorized/page/2/',
array( array(
Expand All @@ -104,6 +123,17 @@ public function data_canonical() {
), ),
), ),
), ),
array(
'/category/?paged=2',
array(
'url' => '/category/page/2/',
'qv' => array(
'category_name' => '',
'paged' => 2,
),
),
61957,
),
array( array(
'/category/uncategorized/?paged=2', '/category/uncategorized/?paged=2',
array( array(
Expand All @@ -127,6 +157,17 @@ public function data_canonical() {
), ),


// Categories & intersections with other vars. // Categories & intersections with other vars.
array(
'/category/?tag=post-formats',
array(
'url' => '/category/?tag=post-formats',
'qv' => array(
'category_name' => '',
'tag' => 'post-formats',
),
),
61957,
),
array( array(
'/category/uncategorized/?tag=post-formats', '/category/uncategorized/?tag=post-formats',
array( array(
Expand All @@ -146,6 +187,7 @@ public function data_canonical() {
), ),


// Taxonomies with extra query vars. // Taxonomies with extra query vars.
array( '/category/page/1/?test=one%20two', '/category/?test=one%20two', 61957 ), // Extra query vars should stay encoded.
array( '/category/cat-a/page/1/?test=one%20two', '/category/cat-a/?test=one%20two', 18086 ), // Extra query vars should stay encoded. array( '/category/cat-a/page/1/?test=one%20two', '/category/cat-a/?test=one%20two', 18086 ), // Extra query vars should stay encoded.


// Categories with dates. // Categories with dates.
Expand Down
2 changes: 1 addition & 1 deletion tests/phpunit/tests/taxonomy.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ public function test_unregister_taxonomy_removes_rewrite_rules() {
$this->assertTrue( unregister_taxonomy( 'foo' ) ); $this->assertTrue( unregister_taxonomy( 'foo' ) );
$this->assertNotContains( '%foo%', $wp_rewrite->rewritecode ); $this->assertNotContains( '%foo%', $wp_rewrite->rewritecode );
$this->assertNotContains( 'bar=', $wp_rewrite->queryreplace ); $this->assertNotContains( 'bar=', $wp_rewrite->queryreplace );
$this->assertCount( --$count_before, $wp_rewrite->rewritereplace ); // Array was reduced by one value. $this->assertCount( $count_before - 2, $wp_rewrite->rewritereplace ); // Array was reduced by two values.
} }


/** /**
Expand Down
Loading