wordpress-develop icon indicating copy to clipboard operation
wordpress-develop copied to clipboard

Implement a filter to customize output on _block_bindings_post_meta_get_value.

Open snehapatil2001 opened this issue 1 year ago β€’ 8 comments

Ticket: https://core.trac.wordpress.org/ticket/61181

Description

  • This PR introduces a filter to the _block_bindings_post_meta_get_value function, allowing developers to customize the output of post meta values.
  • This enhancement provides greater flexibility for developers who need to modify the displayed content of a meta value, such as showing "Free!" when the meta value is 0 or unset.

Changes Made

  • Added Filter Hook: Inserted an apply_filters call before returning the meta value in the _block_bindings_post_meta_get_value function.
  • Filter Name: The filter is named _block_bindings_post_meta_value.

snehapatil2001 avatar Jun 17 '24 11:06 snehapatil2001

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 snehapatil02, cbravobernal, gziolo, santosguillamot, bacoords.

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

github-actions[bot] avatar Jun 17 '24 11:06 github-actions[bot]

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.

github-actions[bot] avatar Jun 17 '24 11:06 github-actions[bot]

Thanks for working on this! πŸ™‚ I believe something like this could make sense, but I am not sure how it should be structured. Some of the concerns I have at this point:

  • This filter would only run in the server, which means it would show a different value in the editor. I assume that could be a problem.
  • Does this only apply to post meta or to any binding?
  • If it only applies to post meta, should we add the filter to the bindings logic or directly to get_post_meta / get_metadata function? Does this only apply to bindings or each time you want to retrieve the field?

SantosGuillamot avatar Jun 26 '24 09:06 SantosGuillamot

If it only applies to post meta, should we add the filter to the bindings logic or directly to get_post_meta / get_metadata function? Does this only apply to bindings or each time you want to retrieve the field?

There is already an existing filter in get_metadata_raw used internally:

get_{$meta_type}_metadata

gziolo avatar Jun 26 '24 10:06 gziolo

Thinking about this a bit more, I feel more confident that we should probably provide a general filter for bindings that receive the source and the same arguments as get_value_callback. Something similar to the render_block filters. This way, it would be possible to change the value not only of post meta but any other source as well.

SantosGuillamot avatar Jul 01 '24 11:07 SantosGuillamot

@SantosGuillamot Introduced a general filter 'block_bindings_source_value' to allow developers to modify the value returned by any block binding source.

Changes Made:

  • Modified the get_value method in the WP_Block_Bindings_Source class to apply the new filter.
  • The filter is applied after the value is retrieved from the source but before it's returned.

Key Benefits:

  1. Flexibility: Works for all binding sources, not just post meta.
  2. Consistency: Functions identically in both the editor and on the front end.
  3. Non-intrusive: It doesn't interfere with existing metadata filters but provides a new layer of customization specific to block bindings.

snehapatil2001 avatar Jul 01 '24 16:07 snehapatil2001

+1 for this concept. I'll give a real world example and you can tell me if you think this feature would help.

I recently wrote a block variation for the image block to pull in the featured image (yes there's a "featured image" block, but the image block has more options, like the lightbox control). I couldn't use the core/postmeta field where the featured image is stored because it returns an attachment ID, not a URL. So I had to create a custom binding source.

It'd be much less overhead if I could have modified the value sent to the block binding API to return the URL of the featured image instead of the attachment ID. But now I have a custom binding source for one simple meta field AND there's no editor preview on my variation.

Feels like this would be a great example of when this feature would be useful.

bacoords avatar Jul 16 '24 13:07 bacoords

Thanks for making the changes πŸ™‚ I agree it makes sense to have a filter like this.

I must say that I had this on my to-do list for a while, but I didn't find time yet with the 6.6 release. I'm really sorry for the wait. I would like to take a deeper look and understand better all the use cases.

In the meantime, would it make sense to add some testing? Is this something done for other similar filters?

Feels like this would be a great example of when this feature would be useful.

I'd need to review your implementation, but at first glance, it looks like a good example for this functionality πŸ™‚

SantosGuillamot avatar Jul 18 '24 08:07 SantosGuillamot

It seems this is close to being ready. Adding some tests and docs, as suggested below, should be enough to include it in the WordPress 6.7 release.

Thanks a lot for working on it @snehapatil2001! Let us know if you'd like some help in that regard πŸ™‚

SantosGuillamot avatar Aug 28 '24 14:08 SantosGuillamot

I left some additional comments clarifying my previous feedback. In addition to that, we still need unit tests to cover the usage as explained in #6839 (review).

Changes addressed. Are we sure we want the filter to return a function instead of a value? It's kind of confusing with the name.

function filter_block_bindings_source_value() {
		return function () {
			return 'Filtered value';
		};
	}

cbravobernal avatar Sep 03 '24 09:09 cbravobernal

I left some additional comments clarifying my previous feedback. In addition to that, we still need unit tests to cover the usage as explained in #6839 (review).

Changes addressed. Are we sure we want the filter to return a function instead of a value? It's kind of confusing with the name.

function filter_block_bindings_source_value() {
		return function () {
			return 'Filtered value';
		};
	}

This is what I tested locally and you don't need to pass a function that returns a function:

Index: tests/phpunit/tests/block-bindings/postMetaSource.php
===================================================================
--- tests/phpunit/tests/block-bindings/postMetaSource.php	(revision 58970)
+++ tests/phpunit/tests/block-bindings/postMetaSource.php	(working copy)
@@ -266,4 +266,41 @@
 			'The post content should not include the script tag.'
 		);
 	}
+
+	/**
+	 * Tests that filter `block_bindings_source_value` is applied.
+	 *
+	 * @ticket 61181
+	 */
+	public function test_filter_block_bindings_source_value() {
+		register_meta(
+			'post',
+			'tests_filter_field',
+			array(
+				'show_in_rest' => true,
+				'single'       => true,
+				'type'         => 'string',
+				'default'      => 'Original value',
+			)
+		);
+
+		$filter_value = function ( $value, $source_name, $source_args ) {
+			if ( 'core/post-meta' !== $source_name ) {
+				return $value;
+			}
+			return "Filtered value: {$source_args['key']}";
+		};
+
+		add_filter( 'block_bindings_source_value', $filter_value, 10, 3 );
+
+		$content = $this->get_modified_post_content( '<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"tests_filter_field"}}}}} --><p>Fallback value</p><!-- /wp:paragraph -->' );
+
+		remove_filter( 'block_bindings_source_value', $filter_value );
+
+		$this->assertSame(
+			'<p>Filtered value: tests_filter_field</p>',
+			$content,
+			'The post content should show the filtered value.'
+		);
+	}
 }

I also updated the test to validate that the args get correctly passed. Like I said in https://github.com/WordPress/wordpress-develop/pull/6839#discussion_r1741739083, there should be another test in render.php that validates that all args are correctly passed to the filter.

gziolo avatar Sep 03 '24 09:09 gziolo

I've been testing it locally and everything seems to be working as expected. Once the changes to the render.php tests are made, I believe this should be ready.

SantosGuillamot avatar Sep 03 '24 12:09 SantosGuillamot

I've been testing it locally and everything seems to be working as expected. Once the changes to the render.php tests are made, I believe this should be ready.

Agreed, the existing test for post meta is nice to keep as it covers 3 params ($filter_value = function ( $value, $source_name, $source_args ) {), but there is one more param to cover, and it can work with a custom source fine, too.

gziolo avatar Sep 03 '24 13:09 gziolo

I've been testing it locally and everything seems to be working as expected. Once the changes to the render.php tests are made, I believe this should be ready.

Agreed, the existing test for post meta is nice to keep as it covers 3 params ($filter_value = function ( $value, $source_name, $source_args ) {), but there is one more param to cover, and it can work with a custom source fine, too.

Done!

cbravobernal avatar Sep 03 '24 14:09 cbravobernal

Committed in https://core.trac.wordpress.org/changeset/58972

cbravobernal avatar Sep 03 '24 16:09 cbravobernal

@gziolo @cbravobernal

I wonder if this might be a simple and proper example of how to filter a meta value using this new filter?

https://gist.github.com/colorful-tones/b6a2dd85e66dd4adb1d58c0ed1623a61#file-demo-block-bindings-php-L43-L62

colorful-tones avatar Oct 01 '24 20:10 colorful-tones

@colorful-tones, it’s correct. The reasoning for this new filter is to allow formatting of the value depending on the context. Some examples:

  • store a price as a number but show with different currencies on the page
  • store timestamp but show formatted date
  • add a translated prefix if the value is set

gziolo avatar Oct 02 '24 05:10 gziolo

Sure, we can use them for future docs and the dev note.

cbravobernal avatar Oct 03 '24 09:10 cbravobernal