Skip to content

Commit

Permalink
Fix query loop bugs by correctly relying on the main query and removi…
Browse files Browse the repository at this point in the history
…ng problematic workaround (#49904)

* Use global main query directly so that global query loop works as expected.

* Remove hack to enforce query loop as it causes problems, and the loop needs to be handled in block templates instead.

* Maintain fix from #43198.

* Clarify logic with comment.

* Add test coverage.
  • Loading branch information
felixarntz authored Aug 29, 2023
1 parent a8eead8 commit 4f6d1b3
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 13 deletions.
6 changes: 0 additions & 6 deletions packages/block-library/src/post-content/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ function render_block_core_post_content( $attributes, $content, $block ) {

$seen_ids[ $post_id ] = true;

// Check is needed for backward compatibility with third-party plugins
// that might rely on the `in_the_loop` check; calling `the_post` sets it to true.
if ( ! in_the_loop() && have_posts() ) {
the_post();
}

// When inside the main loop, we want to use queried object
// so that `the_preview` for the current post can apply.
// We force this behavior by omitting the third argument (post ID) from the `get_the_content`.
Expand Down
6 changes: 0 additions & 6 deletions packages/block-library/src/post-featured-image/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ function render_block_core_post_featured_image( $attributes, $content, $block )
}
$post_ID = $block->context['postId'];

// Check is needed for backward compatibility with third-party plugins
// that might rely on the `in_the_loop` check; calling `the_post` sets it to true.
if ( ! in_the_loop() && have_posts() ) {
the_post();
}

$is_link = isset( $attributes['isLink'] ) && $attributes['isLink'];
$size_slug = isset( $attributes['sizeSlug'] ) ? $attributes['sizeSlug'] : 'post-thumbnail';
$attr = get_block_core_post_featured_image_border_attributes( $attributes );
Expand Down
13 changes: 12 additions & 1 deletion packages/block-library/src/post-template/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,18 @@ function render_block_core_post_template( $attributes, $content, $block ) {
$use_global_query = ( isset( $block->context['query']['inherit'] ) && $block->context['query']['inherit'] );
if ( $use_global_query ) {
global $wp_query;
$query = clone $wp_query;

/*
* If already in the main query loop, duplicate the query instance to not tamper with the main instance.
* Since this is a nested query, it should start at the beginning, therefore rewind posts.
* Otherwise, the main query loop has not started yet and this block is responsible for doing so.
*/
if ( in_the_loop() ) {
$query = clone $wp_query;
$query->rewind_posts();
} else {
$query = $wp_query;
}
} else {
$query_args = build_query_vars_from_query_block( $block, $page );
$query = new WP_Query( $query_args );
Expand Down
86 changes: 86 additions & 0 deletions phpunit/blocks/render-post-template-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,90 @@ public function test_rendering_post_template() {
str_replace( array( "\n", "\t" ), '', $markup )
);
}

/**
* Tests that the `core/post-template` block triggers the main query loop when rendering within a corresponding
* `core/query` block.
*/
public function test_rendering_post_template_with_main_query_loop() {
global $wp_query, $wp_the_query;

// Query block with post template block.
$content = '<!-- wp:query {"query":{"inherit":true}} -->';
$content .= '<!-- wp:post-template {"align":"wide"} -->';
$content .= '<!-- wp:post-title /--><!-- wp:test/in-the-loop-logger /-->';
$content .= '<!-- /wp:post-template -->';
$content .= '<!-- /wp:query -->';

$expected = '<ul class="alignwide wp-block-post-template is-layout-flow wp-block-post-template-is-layout-flow wp-block-query-is-layout-flow">';
$expected .= '<li class="wp-block-post post-' . self::$post->ID . ' post type-post status-publish format-standard hentry category-uncategorized">';
$expected .= '<h2 class="wp-block-post-title">' . self::$post->post_title . '</h2>';
$expected .= '</li>';
$expected .= '</ul>';

// Set main query to single post.
$wp_query = new WP_Query( array( 'p' => self::$post->ID ) );
$wp_the_query = $wp_query;

// Register test block to log `in_the_loop()` results.
$in_the_loop_logs = array();
register_block_type(
'test/in-the-loop-logger',
array(
'render_callback' => static function() use ( &$in_the_loop_logs ) {
$in_the_loop_logs[] = in_the_loop();
return '';
},
)
);

$output = do_blocks( $content );
$this->assertSame( $expected, $output, 'Unexpected parsed blocks content' );
$this->assertSame( array( true ), $in_the_loop_logs, 'Unexpected in_the_loop() result' );
}

/**
* Tests that the `core/post-template` block does not tamper with the main query loop when rendering within a post
* as the main query loop has already been started. In this case, the main query object needs to be cloned to
* prevent an infinite loop.
*/
public function test_rendering_post_template_with_main_query_loop_already_started() {
global $wp_query, $wp_the_query;

// Query block with post template block.
$content = '<!-- wp:query {"query":{"inherit":true}} -->';
$content .= '<!-- wp:post-template {"align":"wide"} -->';
$content .= '<!-- wp:post-title /-->';
$content .= '<!-- /wp:post-template -->';
$content .= '<!-- /wp:query -->';

$expected = '<ul class="alignwide wp-block-post-template is-layout-flow wp-block-post-template-is-layout-flow wp-block-query-is-layout-flow">';
$expected .= '<li class="wp-block-post post-' . self::$post->ID . ' post type-post status-publish format-standard hentry category-uncategorized">';
$expected .= '<h2 class="wp-block-post-title">' . self::$post->post_title . '</h2>';
$expected .= '</li>';
$expected .= '</ul>';

// Update the post's content to have a query block for the same query as the main query.
wp_update_post(
array(
'ID' => self::$post->ID,
'post_content' => $content,
'post_content_filtered' => $content,
)
);

// Set main query to single post.
$wp_query = new WP_Query( array( 'p' => self::$post->ID ) );
$wp_the_query = $wp_query;

// Get post content within main query loop.
$output = '';
while ( $wp_query->have_posts() ) {
$wp_query->the_post();

$output = get_echo( 'the_content' );
}

$this->assertSame( $expected, $output, 'Unexpected parsed post content' );
}
}

0 comments on commit 4f6d1b3

Please sign in to comment.