Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.
Merged
Changes from 1 commit
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
a963247
Add block minimal structure
thealexandrelara Feb 28, 2023
150e090
Add Single Product block icon
thealexandrelara Mar 1, 2023
c4376f3
Add usesContext to block.json
thealexandrelara Mar 3, 2023
f168cf8
Add Save function to Single Product Block registration settings
thealexandrelara Mar 4, 2023
558da33
Add InnerBlocks template to Editor for Single Product Block
thealexandrelara Mar 4, 2023
270ed81
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Mar 8, 2023
cf5e905
Add Product selector to Single Product block
thealexandrelara Mar 8, 2023
ad7b3d1
Add withProduct HOC to the block editor
thealexandrelara Mar 8, 2023
bddcba8
Add save function to the Single Product block
thealexandrelara Mar 9, 2023
f7ffd01
Add renderOnServerSide attribute to Product image
thealexandrelara Mar 9, 2023
85975ee
Add renderOnServerSide attribute to Product Category List block
thealexandrelara Mar 9, 2023
9683707
Add renderOnServerSide attribute to Product Price block
thealexandrelara Mar 9, 2023
aea6171
Add renderOnServerSide attribute to Product Rating block
thealexandrelara Mar 9, 2023
810e578
Add renderOnServerSide attribute to Product Stock Indicator block
thealexandrelara Mar 9, 2023
af44329
Add renderOnServerSide attribute to Product Summary block
thealexandrelara Mar 9, 2023
134673c
Add renderOnServerSide attribute to Product Tag List block
thealexandrelara Mar 9, 2023
44f8d48
Add renderOnServerSide attribute to Product Title block
thealexandrelara Mar 9, 2023
c597452
Add renderOnServerSide attribute to Product Add to Cart, Sales Badge …
thealexandrelara Mar 9, 2023
2b2e967
Reuse editor code for Single Product block
thealexandrelara Mar 9, 2023
e8f0485
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Mar 9, 2023
6aa5dcd
Add Props interface to Single Product Server Side Editor
thealexandrelara Mar 10, 2023
b662d51
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Mar 13, 2023
a9865ab
Add render_block_context hook to SingleProductServerSide block
thealexandrelara Mar 13, 2023
b926dbc
Fix Single Product Server Side block that was not appearing in the bl…
thealexandrelara Mar 14, 2023
09d22b9
Fix bug where the correct product was not being displayed on the fron…
thealexandrelara Mar 14, 2023
b41c45d
Remove Single Product Block
gigitux Mar 15, 2023
d48885e
Merge branch 'trunk' into fix/remove-single-product-block
gigitux Mar 15, 2023
7ce210b
Add Product title and product summary variations
thealexandrelara Mar 15, 2023
daeb00c
Add Add To Cart Form and Product Meta blocks to the Single Product Block
thealexandrelara Mar 15, 2023
bdc22ad
remove more files that belong the Single Product block
gigitux Mar 15, 2023
c57f265
Merge branch 'fix/remove-single-product-block' of https://github.com/…
gigitux Mar 15, 2023
8298400
fix php linter error
gigitux Mar 15, 2023
321b3d7
Merge branch 'trunk' of https://github.com/woocommerce/woocommerce-bl…
gigitux Mar 16, 2023
79b86bc
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Mar 16, 2023
5492bd5
Merge branch 'fix/remove-single-product-block' into feat/add-single-p…
gigitux Mar 16, 2023
4fffeb5
wrap up Single Product Block
gigitux Mar 17, 2023
2567373
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Mar 17, 2023
5cf6e9e
Fix issue preventing the block from working on the Single Product Tem…
thealexandrelara Mar 17, 2023
9af8b21
Fix update context function for Single Product block
thealexandrelara Mar 17, 2023
422423b
Replace Single Product block title
thealexandrelara Mar 17, 2023
2c2b34b
Replace global $post only for Single Product inner blocks
thealexandrelara Mar 17, 2023
5b2334a
Remove unnecessary var_dump statement
thealexandrelara Mar 17, 2023
15f46e8
Improve documentation for the Single Product block
thealexandrelara Mar 17, 2023
e32db09
fix registration: add-to-cart-form and product-meta blocks
gigitux Mar 20, 2023
aabd3f2
Improve documentation for Single Product block
thealexandrelara Mar 17, 2023
8d9f3bd
Fix lint error related to imports
thealexandrelara Mar 20, 2023
583e49e
Remove query monitor debug code
thealexandrelara Mar 20, 2023
b28d10e
Remove unnecessary `renderOnServerSide` attribute from product elements
thealexandrelara Mar 20, 2023
650cb93
Fix return type for the Save function of the Product Elements blocks
thealexandrelara Mar 20, 2023
4c93233
Fix return type for the Save function of the Product Title block
thealexandrelara Mar 20, 2023
0a94903
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Mar 20, 2023
b5a6591
Add scss file for Single Product block
thealexandrelara Mar 20, 2023
815f6d1
Fix PHP coding standards
thealexandrelara Mar 20, 2023
317145e
Fix php coding standards
thealexandrelara Mar 20, 2023
9b024ce
Remove internal dependencies section duplication
thealexandrelara Mar 21, 2023
b877f79
Replace .wc-block with .wc-block-editor- styles
thealexandrelara Mar 21, 2023
adbe4e9
Solve style for the Edit card component in the Single Product block
thealexandrelara Mar 21, 2023
14c87c7
Fix error preventing Block Control from being displayed
thealexandrelara Mar 21, 2023
64aec59
Make the Product title to be a link by default
thealexandrelara Mar 21, 2023
7dfa554
Fix PHP Coding Standards
thealexandrelara Mar 21, 2023
3596e22
Fix PHP Code Standards
thealexandrelara Mar 21, 2023
930a814
Fix php cs issues
thealexandrelara Mar 21, 2023
9095527
Fix issue with php cs
thealexandrelara Mar 21, 2023
07ab19e
Remove unnecessary comment
thealexandrelara Mar 21, 2023
b97bd02
Remove unused Icon import
thealexandrelara Mar 21, 2023
e3fe1c0
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Mar 22, 2023
ecaadec
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Mar 24, 2023
3b66307
Remove unnecessary dot from the className attributes in the Single Pr…
thealexandrelara Mar 24, 2023
3610324
Replace metadata.name with metadata.title in the Editor file
thealexandrelara Mar 24, 2023
c9233af
Ignore phpcs rule
thealexandrelara Mar 24, 2023
83bb365
Add new attributes to the Attributes interface of the Price block
thealexandrelara Mar 24, 2023
6d9dab8
Fix typescript errors on the Product Meta block
thealexandrelara Mar 24, 2023
6e6ecd8
Fix ts errors on Single Product editor
thealexandrelara Mar 24, 2023
8cc11a4
Fix ts errors on layout-editor.tsx
thealexandrelara Mar 24, 2023
561dc19
Fix ts errors in Single Product index file
thealexandrelara Mar 24, 2023
9f83f35
Fix ts errors on add to cart form index file
thealexandrelara Mar 24, 2023
f77ecee
Fix unsupported block error when using inner blocks outside the Singl…
thealexandrelara Mar 28, 2023
2f824f3
Fix unsupported block error for the Single Product block when editing…
thealexandrelara Mar 28, 2023
675f3b1
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Mar 29, 2023
b87d0a5
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Mar 30, 2023
fe807b9
Remove unnecessary JSDoc types
thealexandrelara Mar 30, 2023
31ad081
Remove unnecessary APIError component
thealexandrelara Mar 30, 2023
cd9f9f8
Replace global $post only when rendering a `core/post-excerpt` block
thealexandrelara Mar 31, 2023
8bb4d52
Fix php cs errors
thealexandrelara Mar 31, 2023
667827c
Put the Single Product block behind an experimental flag
thealexandrelara Mar 31, 2023
dadf2dc
Fix error occurring when trying to access ancestor of an undefined bl…
thealexandrelara Mar 31, 2023
d32d65e
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Mar 31, 2023
7d61f35
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Apr 4, 2023
ab6a2bb
Remove JS Doc from shared-product-control.tsx
thealexandrelara Apr 4, 2023
e39d65e
Fix single-product-frontend.js 404 error
thealexandrelara Apr 4, 2023
1e90f34
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Apr 4, 2023
70dfe54
Fix error 'Block names must be string' when adding Single Product block
thealexandrelara Apr 4, 2023
5703ec1
Add todo comment to state the temporary solution used in the Single P…
thealexandrelara Apr 4, 2023
c1bd5dc
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Apr 4, 2023
93e8698
Merge branch 'trunk' into feat/add-single-product-block-server-side
thealexandrelara Apr 5, 2023
a7f84c7
Move the wp_reset_postdata outside the block name check condition
thealexandrelara Apr 6, 2023
70f2892
Add comments about why we need to unregister the block on Single Prod…
thealexandrelara Apr 6, 2023
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
Prev Previous commit
Next Next commit
Replace global $post only for Single Product inner blocks
  • Loading branch information
thealexandrelara committed Mar 17, 2023
commit 2c2b34b229eb626f9f5917b1fc55a0153ac1b04c
43 changes: 37 additions & 6 deletions src/BlockTypes/SingleProduct.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class SingleProduct extends AbstractBlock {
*/
protected $block_name = 'single-product';
protected $product_id = 0;
protected $single_product_inner_blocks_names = [];

/**
* API version name.
Expand Down Expand Up @@ -57,17 +58,47 @@ protected function render( $attributes, $content, $block ) {
* @return array Updated block context.
*/
public function update_context( $context, $block, $parent_block ) {
// var_dump($block['blockName']);

if( $block['blockName'] === 'woocommerce/single-product'
&& isset( $block['attrs']['productId'] ) ) {
$this->product_id = $block['attrs']['productId'];

$this->single_product_inner_blocks_names = array_reverse($this->extract_single_product_inner_block_names( $block ));
}
if( $this->product_id > 0 ){
global $post;
$post = get_post( $this->product_id );
setup_postdata( $post );
$context['postId'] = $this->product_id;
}

$this->replace_post_for_single_product_inner_block( $block, $context );

return $context;
}

protected function extract_single_product_inner_block_names($block, &$result = []) {
if(isset($block['blockName'])){
$result[] = $block['blockName'];
}

if(isset($block['innerBlocks'])){
foreach ($block['innerBlocks'] as $inner_block) {
$this->extract_single_product_inner_block_names($inner_block, $result);
}
}
return $result;
}

protected function replace_post_for_single_product_inner_block( $block, &$context ) {
if( $this->single_product_inner_blocks_names ){
$block_name = array_pop( $this->single_product_inner_blocks_names);

if( $block_name === $block['blockName']){
global $post;
$post = get_post( $this->product_id );
Copy link
Contributor

@nefeline nefeline Mar 24, 2023

Choose a reason for hiding this comment

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

I would encourage finding an alternative approach here rather than overriding the WordPress $post global variable for a few reasons:

  • Overwriting WordPress global variables can cause conflicts and unexpected behavior.
  • When we overwrite global variables, there's a risk of breaking core functionality or interfering with the functionality of plugins and themes that rely on those.
  • Additionally, it can make the code harder to maintain and debug, as it can be challenging to track down where a particular value is being set or changed.

As an alternative, how about updating the render method for each one of the relevant blocks, so they have different behaviors depending on the context? Assuming the $product_id value for the block can be fetched from the DB:

example for the Add to cart form:

ob_start();
// When in the context of the single product template, behave exactly as on Woo core:
if ( is_product() ) {
	while ( have_posts() ) {
		the_post();
		global $product;
		do_action( 'woocommerce_' . $product->get_type() . '_add_to_cart' );
	}
} else {
        // When used anywhere else on the site, fetch the saved product ID selected by the user for this block instead:
	$product = wc_get_product( $product_id );

	if ( ! $product ) {
		return;
	}

	do_action( 'woocommerce_' . $product->get_type() . '_add_to_cart' );
}

$product = ob_get_clean();

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree that this is not the best approach and thank you for suggesting a new one; however, I think this could be handled in a separate PR that would refactor every block to use the approach that we decide on Using the Blockified Single Product Template. Editing the render method would not work for the variations blocks, because we cannot edit their render method :/ (ref: per0F9-qM-p2)

Copy link
Contributor

@nefeline nefeline Mar 30, 2023

Choose a reason for hiding this comment

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

Thank you so much for sharing the link to that discussion and more context about this problem! I added a comment over there. It looks like the only block that is relying on the global post variable is the Post Excerpt. Cross-posting my thoughts over here:

I was taking a look at the proposed solution, which currently is this one:

         protected function replace_post_for_single_product_inner_block( $block, &$context ) {
		if ( $this->single_product_inner_blocks_names ) {
			$block_name = array_pop( $this->single_product_inner_blocks_names );

			if ( $block_name === $block['blockName'] ) {
				global $post;
				// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
				$post = get_post( $this->product_id );
				setup_postdata( $post );
				$context['postId'] = $this->product_id;
			}

			if ( ! $this->single_product_inner_blocks_names ) {
				wp_reset_postdata();
			}
		}
	}

I did a small experiment as while I did understand why we needed to update the $context['postId'] , I didn’t understand why we needed to overwrite the post global variable. The above method was updated to:

	protected function replace_post_for_single_product_inner_block( $block, &$context ) {
		if ( $this->single_product_inner_blocks_names ) {
			$block_name = array_pop( $this->single_product_inner_blocks_names );

			if ( $block_name === $block['blockName'] && ! is_product() ) {
				$context['postId'] = $this->product_id;
			}
		}
	}

With this change in place, the Single Product block worked as expected, and all inner blocks were rendered, except for the Product Summary block, which is a variation of the Post Excerpt block. Next, I checked what’s going on on the Gutenberg side, this is what renders the post excerpt content:

function render_block_core_post_excerpt( $attributes, $content, $block ) {
	if ( ! isset( $block->context['postId'] ) ) {
		return '';
	}

	/*
	* The purpose of the excerpt length setting is to limit the length of both
	* automatically generated and user-created excerpts.
	* Because the excerpt_length filter only applies to auto generated excerpts,
	* wp_trim_words is used instead.
	*/
	$excerpt_length = $attributes['excerptLength'];
	if ( isset( $excerpt_length ) ) {
		$excerpt = wp_trim_words( get_the_excerpt(), $excerpt_length );
	} else {
		$excerpt = get_the_excerpt();
	}
	$more_text           = ! empty( $attributes['moreText'] ) ? '<a class="wp-block-post-excerpt__more-link" href="' . esc_url( get_the_permalink( $block->context['postId'] ) ) . '">' . wp_kses_post( $attributes['moreText'] ) . '</a>' : '';
	$filter_excerpt_more = function( $more ) use ( $more_text ) {
		return empty( $more_text ) ? $more : '';
	};
	/**
	 * Some themes might use `excerpt_more` filter to handle the
	 * `more` link displayed after a trimmed excerpt. Since the
	 * block has a `more text` attribute we have to check and
	 * override if needed the return value from this filter.
	 * So if the block's attribute is not empty override the
	 * `excerpt_more` filter and return nothing. This will
	 * result in showing only one `read more` link at a time.
	 */
	add_filter( 'excerpt_more', $filter_excerpt_more );
	$classes = array();
	if ( isset( $attributes['textAlign'] ) ) {
		$classes[] = 'has-text-align-' . $attributes['textAlign'];
	}
	if ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) {
		$classes[] = 'has-link-color';
	}
	$wrapper_attributes = get_block_wrapper_attributes( array( 'class' => implode( ' ', $classes ) ) );

	$content               = '<p class="wp-block-post-excerpt__excerpt">' . $excerpt;
	$show_more_on_new_line = ! isset( $attributes['showMoreOnNewLine'] ) || $attributes['showMoreOnNewLine'];
	if ( $show_more_on_new_line && ! empty( $more_text ) ) {
		$content .= '</p><p class="wp-block-post-excerpt__more-text">' . $more_text . '</p>';
	} else {
		$content .= " $more_text</p>";
	}
	remove_filter( 'excerpt_more', $filter_excerpt_more );
	return sprintf( '<div %1$s>%2$s</div>', $wrapper_attributes, $content );
}

Note how get_the_excerpt() is invoked without receiving the post_id as a variable. As a consequence, it attempts to fetch the excerpt from the global context (which will be the post where the block was added instead of being based on the block context).

In other words, with a change as simple as get_the_excerpt( $block->context['postId'] ); to the render_block_core_post_excerpt function, we can achieve all goals without overwriting any global variables.

With that said, what are your thoughts about:

  1. Proposing this change to the core of Gutenberg
  2. Relying on a dedicated version for the Post Excerpt in the meantime

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for bringing this solution here, yes I agree with you we can implement your suggestion and it would be much better. I'll be changing the code to match what you suggested and put a conditional to only apply the global post when it is the post excerpt block, at least until GB approves the change to its core, or we have a dedicated version of the Post Excerpt

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nefeline I replaced replace_post_for_single_product_inner_block with your suggested solution, but it gives me an error when I go to the frontend, am I missing something?

image

Copy link
Contributor

@nefeline nefeline Mar 30, 2023

Choose a reason for hiding this comment

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

Sorry about that! I forgot to share a few changes I did to the AddToCartForm to support this; they are available 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.

Thank you for sharing this! Now the error is gone, but I noticed that if I add the block inside the Single Product template and visit a product page, the product that is displayed is not the one that was selected in the Editor, is the product that the user is currently visiting

Copy link
Contributor

Choose a reason for hiding this comment

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

I noticed that if I add the block inside the Single Product template and visit a product page, the product that is displayed is not the one that was selected in the Editor, is the product that the user is currently visiting

Nice catch! That's because of this line: if we ditch the ! is_product() validation we are all set: in reality, it doesn't matter if we are in the context of a single product template or not; we will always want to fetch the content from the block context.

setup_postdata( $post );
$context['postId'] = $this->product_id;
}

if ( ! $this->single_product_inner_blocks_names ) {
wp_reset_postdata();
}
}
}
}