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

Refactor block binding processing and attribute computation #6059

Conversation

michalczaplinski
Copy link
Contributor

@michalczaplinski michalczaplinski commented Feb 7, 2024

This PR refactors the processing of block bindings into 2 steps.

Before

process_block_bindings() takes the $block_content as input and:

  1. Gets the value for each "bound" attribute from the respective source.
  2. For each updated attribute calls replace_html() to get the updated $block_content filled in with the values from the updated attributes.

Now

process_block_bindings() doesn't take any input

  1. Gets the value for each "bound" attribute from the respective source.
  2. Returns the updated attributes with values from the sources.
  3. The updated attributes are used in the render() function of the block where replace_html() is called with the updated attributes.

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


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.

Copy link

github-actions bot commented Feb 7, 2024

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.

@gziolo
Copy link
Member

gziolo commented Feb 7, 2024

This is great as it will allow as to use the computed value from the block bindings inside block attributes passed to the inner blocks through the $parent_block variable and as $this->attributes passed to the render_callback. Inside the render_callback we also will have $block_content that will have the bound attributes already replaced.

- wrap the replace_html with `if ( ! empty( $computed_attributes ) )`
- move the processing of block bindings to the top of render()
- move the logic to update `$this->attributes` inside of `process_block_bindings()`
@draganescu
Copy link

Is there a way to opt out of replace_html and handle the new data coming from the binding in some other special way?

@gziolo
Copy link
Member

gziolo commented Feb 8, 2024

Is there a way to opt out of replace_html and handle the new data coming from the binding in some other special way?

Can you elaborate a bit more on it? I don't think it's part of the changes, but it might be supported regardless with existing filters.

@@ -411,6 +417,9 @@ public function render( $options = array() ) {
)
);

// Process the block bindings and get attributes updated with the values from the sources.
$computed_attributes = $this->process_block_bindings();
Copy link
Member

Choose a reason for hiding this comment

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

I'm comtemplating on my feedback from yesterday and started thinking whether to move back here the changes to update $this->attributes as it now unclear why this call is here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I think that it's a little bit better to keep that type of side-effect
directly in the render() and not hide it in the method, but it's not a strong opinion.

Copy link
Member

Choose a reason for hiding this comment

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

I agree with you. Thank you for trying the alternative approach 🙇

@gziolo
Copy link
Member

gziolo commented Feb 8, 2024

I updated locally unit tests to validate that block attributes get updated correctly:

Index: tests/phpunit/tests/block-bindings/render.php
===================================================================
--- tests/phpunit/tests/block-bindings/render.php	(revision 57564)
+++ tests/phpunit/tests/block-bindings/render.php	(working copy)
@@ -62,6 +62,7 @@
 		$result   = $block->render();
 
 		$this->assertEquals( $expected, $result, 'The block content should be updated with the value returned by the source.' );
+		$this->assertSame( 'test source value', $block->attributes['content'] );
 	}
 
 	/**
@@ -97,5 +98,6 @@
 		$result   = $block->render();
 
 		$this->assertEquals( $expected, $result, 'The block content should be updated with the value returned by the source.' );
+		$this->assertSame( "The attribute name is 'content' and its binding has argument 'key' with value '$value'.", $block->attributes['content'] );
 	}
 }

We could extend the existing tests, but maybe refactor it a bit more. It would also be nice to add a test for the Image block to ensure the url gets correctly replaced when using the saved image placeholder which fixes the issue reported in WordPress/gutenberg#58425:

<!-- wp:image -->
<figure class="wp-block-image"><img alt=""/></figure>
<!-- /wp:image -->

This is the test I drafted that passes:

/**
	 * Tests if the block content is updated with the value returned by the source
	 * for the Image block in the placeholder state.
	 *
	 * @ticket 60282
	 *
	 * @covers ::register_block_bindings_source
	 */
	public function test_update_block_with_value_from_source_imag_placeholder() {
		$get_value_callback = function () {
			return 'https://example.com/image.jpg';
		};

		register_block_bindings_source(
			self::SOURCE_NAME,
			array(
				'label'              => self::SOURCE_LABEL,
				'get_value_callback' => $get_value_callback,
			)
		);

		$block_content = <<<HTML
<!-- wp:image {"metadata":{"bindings":{"url":{"source":"test/source"}}}} -->
<figure class="wp-block-image"><img alt=""/></figure>
<!-- /wp:image -->
HTML;

		$parsed_blocks = parse_blocks( $block_content );
		$block         = new WP_Block( $parsed_blocks[0] );

		$expected = '<figure class="wp-block-image"><img src="https://example.com/image.jpg" alt=""/></figure>';
		$result   = $block->render();

		$this->assertSame( 'https://example.com/image.jpg', $block->attributes['url'] );
		$this->assertEquals( $expected, trim( $result ), 'The block content should be updated with the value returned by the source.' );
	}

@michalczaplinski michalczaplinski marked this pull request as ready for review February 8, 2024 10:34
Copy link

github-actions bot commented Feb 8, 2024

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 czapla, gziolo, andraganescu.

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

@draganescu
Copy link

Can you elaborate a bit more on it? I don't think it's part of the changes, but it might be supported regardless with existing filters.

I am aware I may be asking dumb questions 😁 - so what I meant was what if I have a block and I bind and attribute to some source but I do not want core's processing on the output. Maybe my attribute is a visibility flag and the block has other attributes too bound and I'd short-circuit the binding's replace_html and handle it in my own render callback so avoid useless server work of replacing markup for no reason.

Currently the binding process is completely on, nether $block_content = $this->process_block_bindings( $block_content ); or $modified_block_content = $this->replace_html are filtered. Is it too soon to consider filters? Maybe it is.

@michalczaplinski
Copy link
Contributor Author

@gziolo I've added the unit test that you drafted, thanks! 🙂

I think that this is looking pretty good so let me know your thoughts about the #6059 (comment) . I think that we can always change it in a follow-up.

Copy link
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

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

By the way, I can apply the necessary change discussed before committing tomorrow.

Great work with all refactoring this week 👏

@gziolo
Copy link
Member

gziolo commented Feb 9, 2024

Committed with https://core.trac.wordpress.org/changeset/57574.

@gziolo gziolo closed this Feb 9, 2024
// If there are computed attributes, update the attributes with the
// computed ones.
if ( ! empty( $computed_attributes ) ) {
// Merge the computed attributes with the original attributes
Copy link
Member

Choose a reason for hiding this comment

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

Missing end period.

Copy link
Member

Choose a reason for hiding this comment

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

@gziolo ☝️

Copy link
Member

Choose a reason for hiding this comment

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

I noticed that myself, too. 🙈 I'm working on another commit and I plan to include it. Thank you as always for your attention to detail and checking new commits 🙇🏻

Copy link
Member

Choose a reason for hiding this comment

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

Done in acad00e.

@gziolo
Copy link
Member

gziolo commented Feb 9, 2024

Currently the binding process is completely on, nether $block_content = $this->process_block_bindings( $block_content ); or $modified_block_content = $this->replace_html are filtered. Is it too soon to consider filters? Maybe it is.

@draganescu, we could always discuss introducing new settings for the block binding source:

  • filter_callback to post-process the raw value returned by the source, example: date formatting.
  • should_replace_callback to run before $this->replace_html() that would let folks decide whether to replace the value for an attribute in HTML.

We should be iterating on the public API as we learn about the most common needs reported by developers.

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.

4 participants