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

Product Collection: Parse front-end context #44145

Open
wants to merge 17 commits into
base: trunk
Choose a base branch
from

Conversation

xristos3490
Copy link
Member

@xristos3490 xristos3490 commented Jan 28, 2024

Submission Review Guidelines:

Changes proposed in this Pull Request:

This Pull Request parses the front-end location context for each Product Collection block and passes it to its context.

Context Levels

The context is parsed per block. This happens because it's easier to serve the following priorities:

  • Block-level context: A product selected in the (future) context selector in the inspector controls (Pending; probably in a different PR.)
  • Ancestor-level context: A product fetched by a possible Single Product container block or nested ones.
  • Global: Parses the global wp_query.

Requirements

Possible contexts:

  • Product
  • Cart/Checkout
  • Order
  • Archive
  • Site (generic, empty source data; the Shop page belongs here as well)

All need to be specific and fill in the required source data:

  • Product: The product ID.
  • Cart: The cart contents product IDs.
  • Archive: The taxonomy name and the viewed term ID.
  • Order: The order ID.

Note

By default, context is only retrieved in pages and entities related to WooCommerce. This means that if a post contains a product collection block or a non-related taxonomy like category, the context will be empty and default to site.

Closes #44144

How to test the changes in this Pull Request:

Using the WooCommerce Testing Instructions Guide, include your detailed testing instructions:

Before testing

  1. Add the following PHP snippet to help with identifying the context:
add_filter(
	'render_block_woocommerce/product-template',
	function ( $block_content, $block, $instance ) {
		$location      = $instance->context['productCollectionLocation'] ?? array();
		$block_content = print_r( $location, true ) . $block_content;
		return $block_content;
	},
	0,
	3
);
  1. Insert a product collection block on all pages, including single product templates, the shop page, order confirmation, single pages or posts, cart, and checkout.
  2. Ensure that there is at least one global product attribute and a product using this attribute. To access archives, visit an attribute's edit page by navigating to Products > Attributes, edit an attribute, and check the "Enable Archives" option.

Testing

  1. Start exploring.
  2. Add a Product Collection block to the following places:
  • Shop
  • Single Products
  • Single Posts
  • Within a single Post.
  • Single Product: Template for a specific product.
  • Within a Page.
  • Within a Single Product container block.
  • Within a Pattern; add the pattern somewhere.
  • Within a Synced Pattern; add the pattern somewhere again.
  • Any other cases?
  1. Notice the context on each case printed on top.

Negative testing

  1. Ensure that only woo-related pages are getting a context value.
  2. Test that a simple Post (or any other CPT) or the category tax archive returns the generic context.

Changelog entry

  • Automatically create a changelog entry from the details below.

Significance

  • Patch
  • Minor
  • Major

Type

  • Fix - Fixes an existing bug
  • Add - Adds functionality
  • Update - Update existing functionality
  • Dev - Development related task
  • Tweak - A minor adjustment to the codebase
  • Performance - Address performance issues
  • Enhancement - Improvement to existing functionality

Message

Comment

@github-actions github-actions bot added the plugin: woocommerce Issues related to the WooCommerce Core plugin. label Jan 28, 2024
Copy link
Contributor

github-actions bot commented Jan 28, 2024

Test Results Summary

Commit SHA: a48b3ec

Test 🧪Passed ✅Failed 🚨Broken 🚧Skipped ⏭️Unknown ❔Total 📊Duration ⏱️
API Tests25900202610m 38s
E2E Tests313003803517m 10s

To view the full API test report, click here.
To view the full E2E test report, click here.
To view all test reports, visit the WooCommerce Test Reports Dashboard.

Copy link
Contributor

github-actions bot commented Jan 29, 2024

Hi @kmanijak,

Apart from reviewing the code changes, please make sure to review the testing instructions as well.

You can follow this guide to find out what good testing instructions should look like:
https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions

Copy link
Contributor

@imanish003 imanish003 left a comment

Choose a reason for hiding this comment

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

Hi @chli,
I've reviewed the code of this PR, and it looks good to me so far. However, I'm uncertain about conducting thorough testing since the PR hasn't covered all cases yet. Should I wait for my review until it fully addresses all cases? 🙂

@imanish003
Copy link
Contributor

Also, a quick note—I'm noticing some PHPCS warnings. It seems like there might be an issue with your PHPCS configuration 🤷‍♂️.
image

@xristos3490 xristos3490 changed the title Product Collection: Parse front-end context (POC) Product Collection: Parse front-end context Feb 28, 2024
@xristos3490 xristos3490 marked this pull request as ready for review March 1, 2024 12:12
@xristos3490 xristos3490 self-assigned this Mar 1, 2024
Copy link
Contributor

@imanish003 imanish003 left a comment

Choose a reason for hiding this comment

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

Hey @xristos3490, great work! 🙌

I believe we're almost at the finish line. I've left a couple of minor suggestions for you to consider.

Copy link
Contributor

@imanish003 imanish003 left a comment

Choose a reason for hiding this comment

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

Hey @xristos3490,

I've dropped a few minor comments. I plan to do a bit more testing later today but wanted to share this feedback with you now. Also, it would be wonderful if @kmanijak could also give it a quick review.

Thanks for the amazing work 🙌🏻

Comment on lines 124 to 131
if ( 'woocommerce/product-collection' === $block['blockName']
&& isset( $block['attrs']['query']['isProductCollectionBlock'] ) ) {
$this->product_collection_inner_blocks_names = array_reverse(
$this->extract_inner_block_names( $block )
);
}

$this->provide_location_context_inner_blocks( $block, $context );
Copy link
Contributor

Choose a reason for hiding this comment

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

TBH I found this logic a bit hard to understand. Here is what I understood, please correct me if I am wrong.

  • For example, If I have "On Sale" collection:
    image

  • Then, our logic depends on the fact that render_block_context will be executed in following sequence on Frontend:
    image

  • Based on above fact, we push the items on Stack (product_collection_inner_blocks_names) & then start popping those items.

  • Question: Is it good idea to assume that flow of render_block_context hook will always be like this in future? Am I missing something?

    • Also, looks like we don't have any other good option because we don't have access to any unique ID. We just have access to "blockName".
      image

Copy link
Contributor

Choose a reason for hiding this comment

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

CC: @thealexandrelara As you worked on implementing similar logic in woocommerce/woocommerce-blocks#8610

Copy link
Member Author

Choose a reason for hiding this comment

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

I understand that the current method of working with inner blocks may not be ideal, but it appeared to be functioning with the Single Product block. It's essential to consider that the inner blocks structure in a Product Collection can be anything and that other blocks, such as the no-results block, will also require access to that context, as you mentioned.

Upon reading this comment, it sparked some thoughts that led me to realize that there may be a problem with how the system operates. This issue impacts both the current case and the Single Product block. Upon further investigation, my assumptions proved to be accurate. I have provided a detailed explanation of this problem here:

I will work on developing a better plan to handle edge cases, and hopefully, I can also resolve the issue in the single product container.

Copy link
Member Author

Choose a reason for hiding this comment

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

Upon further testing, it seems the issue here is more severe than I anticipated. This is because every time the woocommerce/product-template block is rendered, it runs through every inner block for each individual product in the query results. As a result, the required execution sequence is being destroyed.

Back to the drawing board.

Update: The existence of a product-template block (either within a Query Loop or a Product Collection block) breaks the Single Product block as well for the same reason - #45342 (comment).

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm glad that it sparked some thoughts and that you were able to identify the edge cases where this approach does not work as expected. It would be great if you could find a simpler and more effective solution to this 🙂

Does this mean that we cannot merge this PR until we have found a better solution?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, regarding #44145 (comment):

How do you feel about using Reflection to access available_context property? Something like this:

$reflector = new ReflectionObject( $parent_block );
$property  = $reflector->getProperty( 'available_context' );
$property->setAccessible( true );
$available_context = $property->getValue( $parent_block );

Copy link
Contributor

Choose a reason for hiding this comment

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

CC: @thealexandrelara As you worked on implementing similar logic in woocommerce/woocommerce-blocks#8610

It's been a while since I last looked at that code 😅 . TBH, when the Single Product block was created, we didn't anticipate the possibility of it being nested within another Single Product block. I believe that if that's a valid use case, we could probably change the logic to support it. Each rendered block has a unique clientId, so perhaps we can use that identifier to differentiate between different block instances and apply the corresponding context for each nested block (I'm not sure if that is actually possible, so I'm just braintorming here).

Copy link
Member Author

Choose a reason for hiding this comment

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

IMO, It would be better to use isProductCollectionBlock instead. You may find some examples by searching isProductCollectionBlock in this file

@imanish003, It seems to me that using the query context is the best choice for this situation. If that's what you meant, then you're absolutely right!

After taking a closer look into how the block context works in PHP, it seems that utilizing WordPress's native context is the most efficient and straightforward approach to bypass the barrier of the protected $available_context.

This guarantees that the location context is only added within the context of blocks that use the "query" context and include the [query][isProductCollectionBlock] value, which helps identify the inner blocks.

I made these changes here: 17de651, and they seem to be working great! Can you take another look?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for chiming in, @thealexandrelara!

we didn't anticipate the possibility of it being nested within another Single Product block

💯 agree that this is an edge case!

For me, the main issue that this PR surfaced is the one I described in #45374. The product-template spawns new blocks during render, which are not accounted for in the block names execution stack, causing a break in the flow when adding the single product's context.

Copy link
Contributor

@kmanijak kmanijak left a comment

Choose a reason for hiding this comment

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

Great work @xristos3490 !

Testing:

  • in post
  • in Single Product block in post
  • in page
  • in Single Product block in page
  • in Category
  • in Single Product block in Category
  • in specific Singe Product
  • in Single Product block in specific Single Product
  • ❌ in Cart - lack of productIds and PHP error - details below
  • in Single Product block in Cart
  • in Products by Category
  • in Single Product block in Products by Category
  • in Products by Tag
  • in Single Product block in Products by Tag
  • in Products by Attribute
  • in Single Product block in Products by Attribute
  • in Order Confirmation
  • in Single Product block in Order Confirmation

  • ❌ in Cart - there's no productIds even though there are products in Cart
image

There's also PHP error:

Screen Shot 2024-03-06 at 12 36 14 PM

@kmanijak kmanijak self-requested a review March 13, 2024 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
plugin: woocommerce Issues related to the WooCommerce Core plugin.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Product Collection: Parse front-end context (POC)
4 participants