Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Product Query: make sure request params are set when enhancing REST query #7796

Merged
merged 4 commits into from
Dec 1, 2022

Conversation

sunyatasattva
Copy link
Contributor

@sunyatasattva sunyatasattva commented Nov 30, 2022

This PR is a follow-up to #7743, which in some cases would introduce errors (which could be fatal under PHP 8.0) on the editor side when the REST API request values would be unexpectedly empty.

Testing

Automated Tests

  • Changes in this PR are covered by Automated Tests.
    • Unit tests
    • E2E tests

User Facing Testing

  1. Create a new page.
  2. Add a new Product Query block.
  3. Make sure the block loads products correctly and that there is no PHP warning.
  4. Reload the page.
  5. Make sure still the blocks loads products correctly and that there is no PHP warning.
  • Do not include in the Testing Notes

WooCommerce Visibility

  • WooCommerce Core
  • Feature plugin
  • Experimental

@sunyatasattva sunyatasattva self-assigned this Nov 30, 2022
@github-actions
Copy link
Contributor

The release ZIP for this PR is accessible via:

https://wcblocks.wpcomstaging.com/wp-content/uploads/woocommerce-gutenberg-products-block-7796.zip

@github-actions
Copy link
Contributor

github-actions bot commented Nov 30, 2022

TypeScript Errors Report

Files with errors: 431
Total errors: 2066

🎉 🎉 This PR does not introduce new TS errors.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 30, 2022

Size Change: 0 B

Total Size: 972 kB

ℹ️ View Unchanged
Filename Size
build/active-filters-frontend.js 7.74 kB
build/active-filters-wrapper-frontend.js 6.01 kB
build/active-filters.js 7.32 kB
build/all-products-frontend.js 11.3 kB
build/all-products.js 33.1 kB
build/all-reviews.js 7.79 kB
build/attribute-filter-frontend.js 22.6 kB
build/attribute-filter-wrapper-frontend.js 7.14 kB
build/attribute-filter.js 12.3 kB
build/blocks-checkout.js 27.1 kB
build/cart-blocks/cart-accepted-payment-methods-frontend.js 1.38 kB
build/cart-blocks/cart-cross-sells-frontend.js 253 B
build/cart-blocks/cart-cross-sells-products--product-add-to-cart-frontend.js 5.48 kB
build/cart-blocks/cart-cross-sells-products-frontend.js 4.74 kB
build/cart-blocks/cart-express-payment--checkout-blocks/express-payment-frontend.js 5.04 kB
build/cart-blocks/cart-express-payment-frontend.js 779 B
build/cart-blocks/cart-items-frontend.js 298 B
build/cart-blocks/cart-line-items--mini-cart-contents-block/products-table-frontend.js 5.29 kB
build/cart-blocks/cart-line-items-frontend.js 1.07 kB
build/cart-blocks/cart-order-summary-frontend.js 1.11 kB
build/cart-blocks/cart-totals-frontend.js 319 B
build/cart-blocks/empty-cart-frontend.js 346 B
build/cart-blocks/filled-cart-frontend.js 781 B
build/cart-blocks/order-summary-coupon-form-frontend.js 1.72 kB
build/cart-blocks/order-summary-discount-frontend.js 2.16 kB
build/cart-blocks/order-summary-fee-frontend.js 273 B
build/cart-blocks/order-summary-heading-frontend.js 455 B
build/cart-blocks/order-summary-shipping-frontend.js 5.99 kB
build/cart-blocks/order-summary-subtotal-frontend.js 274 B
build/cart-blocks/order-summary-taxes-frontend.js 435 B
build/cart-blocks/proceed-to-checkout-frontend.js 1.19 kB
build/cart-frontend.js 46.2 kB
build/cart.js 46.1 kB
build/checkout-blocks/actions-frontend.js 1.77 kB
build/checkout-blocks/billing-address--checkout-blocks/shipping-address-frontend.js 3.89 kB
build/checkout-blocks/billing-address-frontend.js 955 B
build/checkout-blocks/contact-information-frontend.js 1.8 kB
build/checkout-blocks/express-payment-frontend.js 1.13 kB
build/checkout-blocks/fields-frontend.js 344 B
build/checkout-blocks/order-note-frontend.js 1.14 kB
build/checkout-blocks/order-summary-cart-items-frontend.js 3.67 kB
build/checkout-blocks/order-summary-coupon-form-frontend.js 1.89 kB
build/checkout-blocks/order-summary-discount-frontend.js 2.28 kB
build/checkout-blocks/order-summary-fee-frontend.js 276 B
build/checkout-blocks/order-summary-frontend.js 1.11 kB
build/checkout-blocks/order-summary-shipping-frontend.js 6.04 kB
build/checkout-blocks/order-summary-subtotal-frontend.js 273 B
build/checkout-blocks/order-summary-taxes-frontend.js 436 B
build/checkout-blocks/payment-frontend.js 8.31 kB
build/checkout-blocks/shipping-address-frontend.js 1.07 kB
build/checkout-blocks/shipping-methods-frontend.js 5.01 kB
build/checkout-blocks/terms-frontend.js 1.64 kB
build/checkout-blocks/totals-frontend.js 324 B
build/checkout-frontend.js 48.3 kB
build/checkout.js 40.1 kB
build/featured-category.js 13.2 kB
build/featured-product.js 13.4 kB
build/filter-wrapper-frontend.js 13.8 kB
build/filter-wrapper.js 2.4 kB
build/general-style-rtl.css 1.29 kB
build/general-style.css 1.29 kB
build/handpicked-products.js 7.29 kB
build/legacy-template.js 2.85 kB
build/mini-cart-component-frontend.js 20 kB
build/mini-cart-contents-block/empty-cart-frontend.js 366 B
build/mini-cart-contents-block/filled-cart-frontend.js 230 B
build/mini-cart-contents-block/footer-frontend.js 2.95 kB
build/mini-cart-contents-block/items-frontend.js 237 B
build/mini-cart-contents-block/products-table-frontend.js 590 B
build/mini-cart-contents-block/shopping-button-frontend.js 289 B
build/mini-cart-contents-block/title-frontend.js 368 B
build/mini-cart-contents.js 17.1 kB
build/mini-cart-frontend.js 1.88 kB
build/mini-cart.js 4.28 kB
build/price-filter-frontend.js 13.6 kB
build/price-filter-wrapper-frontend.js 7.02 kB
build/price-filter.js 8.37 kB
build/price-format.js 1.19 kB
build/product-add-to-cart--product-button--product-category-list--product-image--product-price--product-r--a0326d00.js 227 B
build/product-add-to-cart--product-button--product-image--product-rating--product-title.js 151 B
build/product-add-to-cart-frontend.js 1.46 kB
build/product-add-to-cart.js 8.37 kB
build/product-best-sellers.js 7.62 kB
build/product-button--product-category-list--product-image--product-price--product-rating--product-sale-b--e17c7c01.js 432 B
build/product-button--product-image--product-rating--product-sale-badge--product-title.js 301 B
build/product-button-frontend.js 2.13 kB
build/product-button.js 3.82 kB
build/product-categories.js 2.36 kB
build/product-category-list-frontend.js 1.13 kB
build/product-category-list.js 502 B
build/product-category.js 8.62 kB
build/product-image-frontend.js 2.16 kB
build/product-image.js 3.93 kB
build/product-new.js 7.62 kB
build/product-on-sale.js 7.95 kB
build/product-price-frontend.js 2.15 kB
build/product-price.js 1.53 kB
build/product-query.js 4.44 kB
build/product-rating-frontend.js 1.44 kB
build/product-rating.js 787 B
build/product-sale-badge-frontend.js 1.38 kB
build/product-sale-badge.js 816 B
build/product-search.js 2.62 kB
build/product-sku-frontend.js 629 B
build/product-sku.js 377 B
build/product-stock-indicator-frontend.js 1.26 kB
build/product-stock-indicator.js 646 B
build/product-summary-frontend.js 1.52 kB
build/product-summary.js 921 B
build/product-tag-list-frontend.js 1.12 kB
build/product-tag-list.js 498 B
build/product-tag.js 8 kB
build/product-title-frontend.js 1.57 kB
build/product-title.js 3.3 kB
build/product-top-rated.js 7.87 kB
build/products-by-attribute.js 8.54 kB
build/rating-filter-frontend.js 7.16 kB
build/rating-filter-wrapper-frontend.js 5.41 kB
build/rating-filter.js 5.79 kB
build/reviews-by-category.js 11.2 kB
build/reviews-by-product.js 12.3 kB
build/reviews-frontend.js 7.02 kB
build/single-product-frontend.js 17.5 kB
build/single-product.js 10 kB
build/stock-filter-frontend.js 7.8 kB
build/stock-filter-wrapper-frontend.js 6.04 kB
build/stock-filter.js 6.71 kB
build/vendors--attribute-filter-wrapper--mini-cart-contents-block/footer--product-add-to-cart-frontend.js 6.86 kB
build/vendors--attribute-filter-wrapper-frontend.js 8.2 kB
build/vendors--cart-blocks/cart-cross-sells-products--cart-blocks/cart-line-items--cart-blocks/cart-order--671ca56f-frontend.js 5.26 kB
build/vendors--cart-blocks/cart-cross-sells-products--cart-blocks/order-summary-shipping--checkout-blocks--18f9376a-frontend.js 19.1 kB
build/vendors--cart-blocks/cart-cross-sells-products--product-add-to-cart-frontend.js 7.54 kB
build/vendors--cart-blocks/cart-line-items--checkout-blocks/order-summary-cart-items--mini-cart-contents---233ab542-frontend.js 3.14 kB
build/vendors--cart-blocks/order-summary-shipping--checkout-blocks/billing-address--checkout-blocks/order--5b8feb0b-frontend.js 4.85 kB
build/vendors--cart-blocks/order-summary-shipping--checkout-blocks/order-summary-shipping--checkout-block--dda5866c-frontend.js 8.85 kB
build/wc-blocks-data.js 18.7 kB
build/wc-blocks-editor-style-rtl.css 5.19 kB
build/wc-blocks-editor-style.css 5.19 kB
build/wc-blocks-google-analytics.js 1.56 kB
build/wc-blocks-middleware.js 934 B
build/wc-blocks-registry.js 2.92 kB
build/wc-blocks-shared-context.js 1.52 kB
build/wc-blocks-shared-hocs.js 1.73 kB
build/wc-blocks-style-rtl.css 24.2 kB
build/wc-blocks-style.css 24.2 kB
build/wc-blocks-vendors-style-rtl.css 1.95 kB
build/wc-blocks-vendors-style.css 1.95 kB
build/wc-blocks-vendors.js 62.4 kB
build/wc-blocks.js 2.63 kB
build/wc-payment-method-bacs.js 816 B
build/wc-payment-method-cheque.js 811 B
build/wc-payment-method-cod.js 909 B
build/wc-payment-method-paypal.js 837 B
build/wc-settings.js 2.6 kB

compressed-size-action

@dinhtungdu
Copy link
Member

dinhtungdu commented Dec 1, 2022

@sunyatasattva I can reproduce the issue, it behaves differently depend on PHP version.

On PHP 7.4, it throws a warning, and the products still show up in the editor preview.

[01-Dec-2022 01:44:50 UTC] PHP Warning:  array_values() expects parameter 1 to be array, null given in .../wp-content/plugins/woocommerce-blocks/src/BlockTypes/ProductQuery.php on line 316

On PHP 8.0, it throws a fatal error, and the editor preview is broken:

[01-Dec-2022 01:47:14 UTC] PHP Fatal error:  Uncaught TypeError: array_reduce(): Argument #1 ($array) must be of type array, null given in .../wp-content/plugins/woocommerce-blocks/src/BlockTypes/ProductQuery.php:312

So I think we should fix this issue by updating get_product_attributes_query to ensure it always returns an array.

	private function get_product_attributes_query( $attributes ) {
		if ( empty( $attributes ) ) {
			return array();
		}
		$grouped_attributes = array_reduce(
			...

@sunyatasattva
Copy link
Contributor Author

Excellent! [Insert ”Why not both?” gif here.] I will update the PR to include both the approaches, as the method can be called independently but also as part of the REST API filter.

@dinhtungdu
Copy link
Member

I will update the PR to include both the approaches, as the method can be called independently but also as part of the REST API filter.

If we return an empty array from get_product_attributes_query() when there is no attribute, then it won't affect the REST API filter because we will merge an empty array, which is equivalent to the check we're adding here.

Copy link
Contributor

@gigitux gigitux left a comment

Choose a reason for hiding this comment

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

It works now! Thanks @sunyatasattva, for creating this PR 🙇

So I think we should fix this issue by updating get_product_attributes_query to ensure it always returns an array.

@dinhtungdu, I would prefer that we add the check ONLY outside of this function. Following the PHPDoc, the function accepts an array, so if it receives another type, it is an expected behavior having the error.

image

Dreaming PHP with a strong type system

$woo_attributes = $request->get_param( '__woocommerceAttributes' );
$on_sale_query = $request->get_param( '__woocommerceOnSale' ) === 'true' ? $this->get_on_sale_products_query() : array();
$orderby_query = isset( $orderby ) ? $this->get_custom_orderby_query( $orderby ) : array();
$tax_query = isset( $woo_attributes ) ? $this->get_product_attributes_query( $woo_attributes ) : array();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is the variable called tax_query and not attributes_query? 🤔

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 mean, just because the return value of get_product_attributes_query returns a tax_query, so I thought it would be a bit more explicit on what's going on when glancing at the code.

Copy link
Member

Choose a reason for hiding this comment

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

If in the future, we have more than one query that returns tax_query, then we will need to update the naming here.

If we follow this way, then I should update (again 😄 ) #7682 to use $meta_query for stock status.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point, it makes sense to rename it attributes_query.

@github-actions github-actions bot added this to the 9.1.0 milestone Dec 1, 2022
@sunyatasattva sunyatasattva added skip-changelog PRs that you don't want to appear in the changelog. type: bug The issue/PR concerns a confirmed bug. block-type: product-query Issues related to/affecting all product-query variations. labels Dec 1, 2022
@sunyatasattva
Copy link
Contributor Author

Hey folks, thanks for the review!

Sorry @dinhtungdu for yesterday, I was quite tired as it was late for me, so I wasn't very lucid. I still am not sure how to exactly reproduce, so please feel free to correct the testing steps above.

Otherwise, I'm converting the PR to Ready for Review and giving you a little time to see if you agree with @gigitux's statement here.

I don't have a strong opinion: I see @gigitux's point on it being expected to fail, but I also see no harm in making it innocuous when it does (maybe the harm is that what we swallow the real problem and the exception).

@sunyatasattva sunyatasattva marked this pull request as ready for review December 1, 2022 09:38
@rubikuserbot rubikuserbot requested a review from a team December 1, 2022 09:38
Copy link
Member

@dinhtungdu dinhtungdu left a comment

Choose a reason for hiding this comment

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

@dinhtungdu, I would prefer that we add the check ONLY outside of this function. Following the PHPDoc, the function accepts an array, so if it receives another type, it is an expected behavior having the error.

The reason I suggested doing so is: if we check the argument, the logic of update_rest_query is easier to read because the flow isn't interrupted by shorthand conditions. I share @sunyatasattva point that I also see no harm in making it innocuous.

But I see your point and I agree, as being explicit may help us more in the long run. Just want to make sure we follow this convention at least inside the ProductQuery class (I will need to update my PRs too).

I still am not sure how to exactly reproduce, so please feel free to correct the testing steps above.

The testing instruction works for me, but I think we should mention the PHP version in the testing instruction too. We should also make sure for testers to enable debug and debug log to see the PHP warnings for PHP 7.4.

$woo_attributes = $request->get_param( '__woocommerceAttributes' );
$on_sale_query = $request->get_param( '__woocommerceOnSale' ) === 'true' ? $this->get_on_sale_products_query() : array();
$orderby_query = isset( $orderby ) ? $this->get_custom_orderby_query( $orderby ) : array();
$tax_query = isset( $woo_attributes ) ? $this->get_product_attributes_query( $woo_attributes ) : array();
Copy link
Member

Choose a reason for hiding this comment

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

If in the future, we have more than one query that returns tax_query, then we will need to update the naming here.

If we follow this way, then I should update (again 😄 ) #7682 to use $meta_query for stock status.

src/BlockTypes/ProductQuery.php Outdated Show resolved Hide resolved
@sunyatasattva
Copy link
Contributor Author

sunyatasattva commented Dec 1, 2022

The reason I suggested doing so is: if we check the argument, the logic of update_rest_query is easier to read because the flow isn't interrupted by shorthand conditions. I share @sunyatasattva point that I also see no harm in making it innocuous.

But I see your point and I agree, as being explicit may help us more in the long run. Just want to make sure we follow this convention at least inside the ProductQuery class (I will need to update my PRs too).

What if I update the get_product_attributes_query( $attributes ) function to be get_product_attributes_query( $attributes = array() )? Would that be a reasonable middle ground? It will still fail like @gigitux mentions if the type is incorrect, but will catch the more common case of an undefined/nullish value and spit out an empty array by default like @dinhtungdu mentions.

@dinhtungdu
Copy link
Member

dinhtungdu commented Dec 1, 2022

What if I update the get_product_attributes_query( $attributes ) function to be get_product_attributes_query( $attributes = array() )? Would that be a reasonable middle ground? It will still fail like @gigitux mentions if the type is incorrect, but will catch the more common case of an undefined/nullish value and spit out an empty array by default like @dinhtungdu mentions.

Sounds like a win-win to me, so I agree 💯 .

@sunyatasattva
Copy link
Contributor Author

@gigitux are you ok with this? I'm updating the PR but can always revert in case you don't like it.

@gigitux
Copy link
Contributor

gigitux commented Dec 1, 2022

@gigitux are you ok with this? I'm updating the PR but can always revert in case you don't like it.

It's super fine for me! Thanks for updating the PR 🙇

Also rename attributes query variable name.
@sunyatasattva
Copy link
Contributor Author

Cool! I've also renamed the variable following your suggestion!

Copy link
Member

@dinhtungdu dinhtungdu left a comment

Choose a reason for hiding this comment

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

LGTM! The failed tests are related to translation and some flaky tests.

@sunyatasattva sunyatasattva merged commit d3a846a into trunk Dec 1, 2022
@sunyatasattva sunyatasattva deleted the fix/pq-rest-query-safety-guards branch December 1, 2022 15:42
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
block-type: product-query Issues related to/affecting all product-query variations. skip-changelog PRs that you don't want to appear in the changelog. type: bug The issue/PR concerns a confirmed bug.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants