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

Query terms by slug #36785

Open
Tracked by #41405
iandunn opened this issue Nov 23, 2021 · 7 comments
Open
Tracked by #41405

Query terms by slug #36785

iandunn opened this issue Nov 23, 2021 · 7 comments
Labels
[Block] Query Loop Affects the Query Loop Block [Type] Enhancement A suggestion for improvement.

Comments

@iandunn
Copy link
Member

iandunn commented Nov 23, 2021

What problem does this address?

Currently the Query block requires hardcoding categoryIds and tagIds, which isn't always portable across development environments.

One example is when a limited set of content is exported from production, to avoid leaking private data into local environments. When only exporting a specific post type, the WRX export file won't include meta data for categories. Instead, it includes the slug, and new categories are created with the next available ID.

When that happens, the local category IDs don't match production, and templates that work in one environment don't work in the other.

I assume there are other situations where hardcoding IDs is undesirable too.

What is your proposed solution?

Support querying by term slugs in addition to IDs.

That can still lead to problems in some situations, but it's much more portable, and much easier to fix when something does go wrong.

@iandunn iandunn added [Type] Enhancement A suggestion for improvement. [Block] Query Loop Affects the Query Loop Block labels Nov 23, 2021
@tellyworth
Copy link
Contributor

I found a way to work around this using a filter. It's a hack, and I definitely think this should be supported in core, but this might be of use for block theme development in the meantime:

function custom_query_block_attributes( $parsed_block ) {
	if ( 'core/query' === $parsed_block['blockName'] ) {
		// If the block has a `category` attribute, then find the corresponding cat ID and set the `categoryIds` attribute.
		// TODO: support multiple?
		if ( isset( $parsed_block[ 'attrs' ][ 'query' ][ 'category' ] ) ) {
			$category = get_category_by_slug( $parsed_block[ 'attrs' ][ 'query' ][ 'category' ] );
			if ( $category ) {
				$parsed_block[ 'attrs' ][ 'query' ][ 'categoryIds' ] = [ $category->term_id ];
			}
		}
	}

	return $parsed_block;
}
add_filter( 'render_block_data', 'custom_query_block_attributes' );

That's only lightly tested, and I haven't considered security.

@Chouby
Copy link
Contributor

Chouby commented Nov 29, 2021

This idea supposes that for a given slug, there is one unique term per taxonomy. That's of course true on a default install. But the limitation that ensured this uniqueness has been removed since version 4.x (I don't remember exactly which one) when shared terms have been splitted. The uniqueness can now be ensured per taxonomy and per one or additionnal parameters that could be added by a plugin. It's thus now possible that a function like get_category_by_slug() or more generally the underlying function get_term_by( 'slug', ... ) doesn't always return the expected result as unfortunately they were never adapted to this new possibility.

@g-elwell
Copy link

g-elwell commented Dec 9, 2021

Querying by slug instead of term ID would mean that if a term slug is updated by a user existing query blocks referencing the term would no longer run the correct query.

Querying by term ID offers some protection here, since this cannot be changed by users.

@iandunn
Copy link
Member Author

iandunn commented Dec 14, 2021

This idea supposes that for a given slug, there is one unique term per taxonomy

Are you referring to https://core.trac.wordpress.org/ticket/22023 ? It seems like https://core.trac.wordpress.org/changeset/30238 protects against duplicate slugs within a taxonomy, but I'd be interested in hearing more. Can you link to a ticket or post that goes into detail?

Otherwise, that seems like a situation that Core doesn't support.

xref core changes: 4.1, 4.2, 4.3, 4.4


if a term slug is updated by a user existing query blocks referencing the term would no longer run the correct query

That's correct, but there are also downsides to querying by ID. Offering both options lets the developer choose which tradeoffs are best for their context.

@tellyworth
Copy link
Contributor

I agree, supporting both IDs and slugs allows theme devs to choose appropriately. IDs make sense when a theme is used on a single site. Slugs make sense for a downloadable theme that's designed to work anywhere.

@dkrape
Copy link

dkrape commented Nov 9, 2022

Agreed on this. Querying by tag name would be very useful for syncing across working environments. In my case, I have dev, staging, and production environments and am using Git to sync templates. However, hard-coded IDs for categories (and also for navigation) makes my dev process very cumbersome.

Replicating the database across the environments just to address hard-coded numerical IDs isn't ideal for several reasons (for example, I want to have testing data in dev but not prod, syncing adds increased and largely unnecessary complexity).

Also, I tried @tellyworth 's fix (adjusted for the new taxQuery structure), but I was unable to get it to work in 6.1. It appears to modify the query array correctly and it fails silently, so I'm not sure the issue. Perhaps render_block_data has changed?

If anyone else has an idea, here's my code:

function custom_query_block_attributes( $parsed_block ) {
	if ( 'core/query' === $parsed_block['blockName'] ) {

		if(is_array($parsed_block['attrs']['query']['taxQuery']['category'])) {
			$category_slugs = $parsed_block['attrs']['query']['taxQuery']['category'];
			$category_ids = [];
			foreach($category_slugs as $slug) {
				if(!is_numeric($slug)) {
					$category = get_category_by_slug( $slug );
					if($category) {
						array_push($category_ids, $category->term_id);
					}
				}
			}
			$parsed_block['attrs']['query']['taxQuery']['category'] = $category_ids;
		}
	}
	return $parsed_block;
}
add_filter( 'render_block_data', 'custom_query_block_attributes' );

@tomjn
Copy link
Contributor

tomjn commented Nov 10, 2022

I've made a quick modification of the above code to do several things:

  • bugfix: it assumes only slugs are used, now it instead adds numeric slugs and converts string slugs
  • feature: made it taxonomy agnostic, it no longer assumes categories but instead runs on all taxonomies specified
  • refactored the code to use guards instead and reduced complexity by returning early
  • WP coding standards formatting
  • typehints for the arguments and return values
function custom_query_block_attributes( array $parsed_block ) : array {
	if ( 'core/query' !== $parsed_block['blockName'] ) {
		return $parsed_block;
	}

	if ( ! is_array( $parsed_block['attrs']['query']['taxQuery'] ) ) {
		return $parsed_block;
	}

	$taxQuery = $parsed_block['attrs']['query']['taxQuery'];
	foreach ( $taxQuery as $tax => $slugs ) {
		$ids = [];
		foreach ( $slugs as $slug ) {
			if ( is_numeric( $slug ) ) {
				array_push( $ids, $slug );
				continue;
			}
			$term = get_term_by( 'slug', $slug, $tax );
			if ( $term instanceof WP_Term ) {
				array_push( $ids, $term->term_id );
			}
		}
		$parsed_block['attrs']['query']['taxQuery'][ $tax ] = $ids;
	}

	return $parsed_block;
}
add_filter( 'render_block_data', 'custom_query_block_attributes' );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Query Loop Affects the Query Loop Block [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

No branches or pull requests

6 participants