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

Product Query: Fix pagination issue #7109

Merged
merged 2 commits into from
Sep 15, 2022
Merged
Changes from all commits
Commits
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
43 changes: 12 additions & 31 deletions src/BlockTypes/ProductQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,27 +37,6 @@ protected function initialize() {

}

/**
* Remove the query block filter and parse the custom query
*
* This function is supposed to be called by the `query_loop_block_query_vars`
* filter. It de-registers the filter to make sure it runs only once and doesn't end
* up hi-jacking future Query Loop blocks.
*
* It needs unfortunately to be `public` or otherwise the filter can't call it.
*
* @param WP_Query $query The WordPress Query.
* @return array
*/
public function get_query_by_attributes_once( $query ) {
remove_filter(
'query_loop_block_query_vars',
array( $this, 'get_query_by_attributes_once' ),
10
);

return $this->get_query_by_attributes( $query, $this->parsed_block );
}

/**
* Update the query for the product query block.
Expand All @@ -66,28 +45,30 @@ public function get_query_by_attributes_once( $query ) {
* @param array $parsed_block The block being rendered.
*/
public function update_query( $pre_render, $parsed_block ) {
if ( 'core/query' !== $parsed_block['blockName'] || ! isset( $parsed_block['attrs']['__woocommerceVariationProps'] ) ) {
if ( 'core/query' !== $parsed_block['blockName'] ) {
return;
}

$this->parsed_block = $parsed_block;

add_filter(
'query_loop_block_query_vars',
array( $this, 'get_query_by_attributes_once' ),
10,
1
);
if ( isset( $parsed_block['attrs']['__woocommerceVariationProps'] ) ) {
add_filter(
'query_loop_block_query_vars',
array( $this, 'get_query_by_attributes' ),
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we make this method inline? This way we don't need to set $this->parsed_block anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, we shouldn't.

This way we don't need to set $this->parsed_block anymore.

It is still necessary this. If we don't use this anymore, the $parsed_block will always be an instance of our variations (for the check at line 54). This is a problem because, for example, if on the same page, we have:

  • Products Query block.
  • Query Loop block with post type set to Post (so it should render the post on frontend).

The filter injects in the Query Loop block the query used by the Products Query block. Obviously, this is incorrect.

Let me know what you think, and if it makes sense to you, I will merge this PR!

Copy link
Contributor

Choose a reason for hiding this comment

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

Re-reading this code, I wonder… is there any way we can make these lines conditional?

https://github.com/woocommerce/woocommerce-blocks/pull/7109/files#diff-efb947e345af5e501dfaa2980b055b3b31c308b31e8db23049b6b48fecee33d8L31-L36

Like, is there any other filter we can use so that we don't run this on every block? It would be amazing if there ware a filter like rest_{$this->post_type}_query, but with something like pre_render_{$this->block_id}_block. You know what I mean? 🤔

Copy link
Contributor Author

@gigitux gigitux Sep 15, 2022

Choose a reason for hiding this comment

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

Re-reading this code, I wonder… is there any way we can make these lines conditional?

GitHub doesn't highlight the lines. From the URL, it seems that you are referring to these:

image

If yes, we already checked during one of our pair sessions: these lines are executed once: when the class has been instanced.

Like, is there any other filter we can use so that we don't run this on every block? It would be amazing if there ware a filter like rest_{$this->post_type}query, but with something like pre_render{$this->block_id}_block. You know what I mean? 🤔

Yeah, I searched during my exploration, but unfortunately doesn't exist.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oooh, right, thanks for the explanation. Another reason not to use an inline function is to avoid the same filter being added several times.

Copy link
Contributor

Choose a reason for hiding this comment

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

I might have been confused. But with this PR, the filter isn't added for each block instance, that's because we are calling a class method so every time add_filter() is called, it knows it's the same method. But if the function was inline, it would be added as a filter every time it's called. (I might be wrong, though)

Copy link
Contributor

Choose a reason for hiding this comment

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

If yes, we already checked during one of our pair sessions: these lines are executed once: when the class has been instanced.

@gigitux Sorry, I wasn't clear enough. I know these lines get executed once in the sense that the filter is registered only once. But the filter is run on the pre-render of every block. Of course we return early and no harm is done. But I still would have loved if it wasn't like that…

I hope this makes more sense.

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 hope this makes more sense.

It makes sense!

Of course we return early and no harm is done. But I still would have loved if it wasn't like that…
Agree, but it seems that it isn't possible :(

I might have been confused. But with this PR, the filter isn't added for each block instance, that's because we are calling a class method so every time add_filter() is called, it knows it's the same method. But if the function was inline, it would be added as a filter every time it's called. (I might be wrong, though)

The filter query_loop_block_query_vars is added every time that the Product Query variation is rendered.
The reason is that pre_render_block is triggered on the pre-render of every block.

Does it make sense?

cc @sunyatasattva

Copy link
Contributor

Choose a reason for hiding this comment

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

The filter query_loop_block_query_vars is added every time that the Product Query variation is rendered.
The reason is that pre_render_block is triggered on the pre-render of every block.

I think the confusion here comes from what we understand by "the filter is added". It's true that add_filter() is called every time a Product Query variation is rendered. But given that we add a named function (in this case, a class method) filter, once it's added once, it isn't added again every time. Ie: if there are ten Product Query blocks in a page, get_query_by_attributes() won't be called 10 times per block, only 1. But if the filter added an inline function instead of a named function, get_query_by_attributes() would be called 10 times for every block. At least, that's how I understand it works, but I might be wrong.

Copy link
Contributor Author

@gigitux gigitux Sep 15, 2022

Choose a reason for hiding this comment

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

It makes sense! I did a test, and I can confirm what you wrote!
Just to be clear, in our case the function is called 4 times for each block. The reason is that the function gutenberg_build_query_vars_from_query_block (where there is the filter query_loop_block_query_vars) in the hierarchy of the Query Block is called 4 times:

This means that if on the page there are two Product Query blocks, the function get_query_by_attributes() is called 8 times.

10,
1
);
}
}

/**
* Return a custom query based on the attributes.
*
* @param WP_Query $query The WordPress Query.
* @param WP_Block $parsed_block The block being rendered.
* @param WP_Query $query The WordPress Query.
* @return array
*/
public function get_query_by_attributes( $query, $parsed_block ) {
public function get_query_by_attributes( $query ) {
$parsed_block = $this->parsed_block;
if ( ! isset( $parsed_block['attrs']['__woocommerceVariationProps'] ) ) {
return $query;
}
Expand Down