From 623f2686969f8cf95083cb564e3284428f272560 Mon Sep 17 00:00:00 2001 From: Sarah Norris <1645628+mikachan@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:31:23 +0100 Subject: [PATCH] Query Loop: Default to querying posts when on singular content (#65067) * Fix linter warnings * Replace the postType if current post type is different * Remove currentPostType logic * Default to posts if is_singular * Update test_rendering_post_template_with_main_query_loop_already_started test * Add test for query loop not inside a singular query * Revert changes to QueryContent * Show query type control only when on a template * Move template logic to QueryInspectorControls * Ensure inherit value is updated when not in a template * Update comment * Rename showDefaultControl to isTemplate * Get postType from context * Add a check for singular content based on available post type * Move inherit reset to a useEffect * Move isTemplate logic to QueryContent * Fix lint warnings Unlinked contributors: alaczek, autumnfjeld. Co-authored-by: mikachan Co-authored-by: fabiankaegy Co-authored-by: creativecoder Co-authored-by: jameskoster Co-authored-by: richtabor Co-authored-by: jasmussen --- .../block-library/src/post-template/index.php | 5 ++ packages/block-library/src/query/block.json | 1 + .../query/edit/inspector-controls/index.js | 20 ++++-- .../src/query/edit/query-content.js | 43 ++++++++++-- phpunit/blocks/render-post-template-test.php | 70 +++++++++++++++++-- 5 files changed, 122 insertions(+), 17 deletions(-) diff --git a/packages/block-library/src/post-template/index.php b/packages/block-library/src/post-template/index.php index 9126355c096a5..64cdd156a5431 100644 --- a/packages/block-library/src/post-template/index.php +++ b/packages/block-library/src/post-template/index.php @@ -64,6 +64,11 @@ function render_block_core_post_template( $attributes, $content, $block ) { if ( in_the_loop() ) { $query = clone $wp_query; $query->rewind_posts(); + + // If in a single post of any post type, default to the 'post' post type. + if ( is_singular() ) { + query_posts( array( 'post_type' => 'post' ) ); + } } else { $query = $wp_query; } diff --git a/packages/block-library/src/query/block.json b/packages/block-library/src/query/block.json index 9eb8495963823..22bfa7b713801 100644 --- a/packages/block-library/src/query/block.json +++ b/packages/block-library/src/query/block.json @@ -41,6 +41,7 @@ "default": false } }, + "usesContext": [ "postType" ], "providesContext": { "queryId": "queryId", "query": "query", diff --git a/packages/block-library/src/query/edit/inspector-controls/index.js b/packages/block-library/src/query/edit/inspector-controls/index.js index 6c246ab89b3b3..4085128e9aef1 100644 --- a/packages/block-library/src/query/edit/inspector-controls/index.js +++ b/packages/block-library/src/query/edit/inspector-controls/index.js @@ -45,7 +45,7 @@ import { useToolsPanelDropdownMenuProps } from '../../../utils/hooks'; const { BlockInfo } = unlock( blockEditorPrivateApis ); export default function QueryInspectorControls( props ) { - const { attributes, setQuery, setDisplayLayout } = props; + const { attributes, setQuery, setDisplayLayout, isTemplate } = props; const { query, displayLayout } = attributes; const { order, @@ -103,6 +103,7 @@ export default function QueryInspectorControls( props ) { if ( ! hasFormatSupport ) { updateQuery.format = []; } + setQuery( updateQuery ); }; const [ querySearch, setQuerySearch ] = useState( query.search ); @@ -118,20 +119,25 @@ export default function QueryInspectorControls( props ) { onChangeDebounced(); return onChangeDebounced.cancel; }, [ querySearch, onChangeDebounced ] ); - const showInheritControl = isControlAllowed( allowedControls, 'inherit' ); + + const showInheritControl = + isTemplate && isControlAllowed( allowedControls, 'inherit' ); const showPostTypeControl = - ! inherit && isControlAllowed( allowedControls, 'postType' ); + ( ! inherit && isControlAllowed( allowedControls, 'postType' ) ) || + ! isTemplate; const postTypeControlLabel = __( 'Post type' ); const postTypeControlHelp = __( 'Select the type of content to display: posts, pages, or custom post types.' ); const showColumnsControl = false; const showOrderControl = - ! inherit && isControlAllowed( allowedControls, 'order' ); + ( ! inherit && isControlAllowed( allowedControls, 'order' ) ) || + ! isTemplate; const showStickyControl = - ! inherit && - showSticky && - isControlAllowed( allowedControls, 'sticky' ); + ( ! inherit && + showSticky && + isControlAllowed( allowedControls, 'sticky' ) ) || + ( showSticky && ! isTemplate ); const showSettingsPanel = showInheritControl || showPostTypeControl || diff --git a/packages/block-library/src/query/edit/query-content.js b/packages/block-library/src/query/edit/query-content.js index 4624b3b96049e..8b3ff09b17934 100644 --- a/packages/block-library/src/query/edit/query-content.js +++ b/packages/block-library/src/query/edit/query-content.js @@ -3,7 +3,7 @@ */ import { useSelect, useDispatch } from '@wordpress/data'; import { useInstanceId } from '@wordpress/compose'; -import { useEffect } from '@wordpress/element'; +import { useEffect, useCallback } from '@wordpress/element'; import { BlockControls, InspectorControls, @@ -32,6 +32,7 @@ export default function QueryContent( { openPatternSelectionModal, name, clientId, + context, } ) { const { queryId, @@ -41,6 +42,7 @@ export default function QueryContent( { tagName: TagName = 'div', query: { inherit } = {}, } = attributes; + const { postType } = context; const { __unstableMarkNextChangeAsNotPersistent } = useDispatch( blockEditorStore ); const instanceId = useInstanceId( QueryContent ); @@ -48,6 +50,16 @@ export default function QueryContent( { const innerBlocksProps = useInnerBlocksProps( blockProps, { template: TEMPLATE, } ); + const isTemplate = useSelect( + ( select ) => { + const currentTemplate = + select( coreStore ).__experimentalGetTemplateForLink()?.type; + const isInTemplate = 'wp_template' === currentTemplate; + const isInSingularContent = postType !== undefined; + return isInTemplate && ! isInSingularContent; + }, + [ postType ] + ); const { postsPerPage } = useSelect( ( select ) => { const { getSettings } = select( blockEditorStore ); const { getEntityRecord, getEntityRecordEdits, canUser } = @@ -81,6 +93,10 @@ export default function QueryContent( { // Changes in query property (which is an object) need to be in the same callback, // because updates are batched after the render and changes in different query properties // would cause to override previous wanted changes. + const updateQuery = useCallback( + ( newQuery ) => setAttributes( { query: { ...query, ...newQuery } } ), + [ query, setAttributes ] + ); useEffect( () => { const newQuery = {}; // When we inherit from global query always need to set the `perPage` @@ -90,11 +106,24 @@ export default function QueryContent( { } else if ( ! query.perPage && postsPerPage ) { newQuery.perPage = postsPerPage; } + // We need to reset the `inherit` value if not in a template, as queries + // are not inherited when outside a template (e.g. when in singular content). + if ( ! isTemplate && query.inherit ) { + newQuery.inherit = false; + } if ( !! Object.keys( newQuery ).length ) { __unstableMarkNextChangeAsNotPersistent(); updateQuery( newQuery ); } - }, [ query.perPage, postsPerPage, inherit ] ); + }, [ + query.perPage, + postsPerPage, + inherit, + isTemplate, + query.inherit, + __unstableMarkNextChangeAsNotPersistent, + updateQuery, + ] ); // We need this for multi-query block pagination. // Query parameters for each block are scoped to their ID. useEffect( () => { @@ -102,9 +131,12 @@ export default function QueryContent( { __unstableMarkNextChangeAsNotPersistent(); setAttributes( { queryId: instanceId } ); } - }, [ queryId, instanceId ] ); - const updateQuery = ( newQuery ) => - setAttributes( { query: { ...query, ...newQuery } } ); + }, [ + queryId, + instanceId, + __unstableMarkNextChangeAsNotPersistent, + setAttributes, + ] ); const updateDisplayLayout = ( newDisplayLayout ) => setAttributes( { displayLayout: { ...displayLayout, ...newDisplayLayout }, @@ -135,6 +167,7 @@ export default function QueryContent( { setDisplayLayout={ updateDisplayLayout } setAttributes={ setAttributes } clientId={ clientId } + isTemplate={ isTemplate } /> diff --git a/phpunit/blocks/render-post-template-test.php b/phpunit/blocks/render-post-template-test.php index e375758044681..6241f6f060516 100644 --- a/phpunit/blocks/render-post-template-test.php +++ b/phpunit/blocks/render-post-template-test.php @@ -113,9 +113,10 @@ public function test_rendering_post_template_with_main_query_loop() { } /** - * Tests that the `core/post-template` block does not tamper with the main query loop when rendering within a post + * Tests that the `core/post-template` block does not tamper with the main query loop when rendering within a single 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. + * Also tests that the default query returns posts of the 'post' post type when in a single post of any post type. */ public function test_rendering_post_template_with_main_query_loop_already_started() { global $wp_query, $wp_the_query; @@ -127,10 +128,18 @@ public function test_rendering_post_template_with_main_query_loop_already_starte $content .= ''; $content .= ''; - $expected = '
    '; - $expected .= '
  • '; - $expected .= '

    ' . self::$post->post_title . '

    '; - $expected .= '
  • '; + $expected = '
      '; + + // Find all the posts of the 'post' post type. + $wp_query_posts = new WP_Query( array( 'post_type' => 'post' ) ); + + while ( $wp_query_posts->have_posts() ) { + $wp_query_posts->the_post(); + $expected .= '
    • '; + $expected .= '

      ' . get_the_title() . '

      '; + $expected .= '
    • '; + } + $expected .= '
    '; // Update the post's content to have a query block for the same query as the main query. @@ -156,4 +165,55 @@ public function test_rendering_post_template_with_main_query_loop_already_starte $this->assertSame( $expected, $output, 'Unexpected parsed post content' ); } + + /** + * Tests that the `core/post-template` block rewinds the default query when not in a single post of any post type. + */ + public function test_rendering_post_template_with_main_query_loop_not_single_post() { + global $wp_query, $wp_the_query; + + // Query block with post template block. + $content = ''; + $content .= ''; + $content .= ''; + $content .= ''; + $content .= ''; + + $expected = '
      '; + + // Find all the posts of the 'post' post type. + $wp_query_posts = new WP_Query( array( 'post_type' => 'post' ) ); + + while ( $wp_query_posts->have_posts() ) { + $wp_query_posts->the_post(); + $expected .= '
    • '; + $expected .= '

      ' . get_the_title() . '

      '; + $expected .= '
    • '; + } + + $expected .= '
    '; + + // 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 all posts. + $wp_query = new WP_Query( array( 'post_type' => 'post' ) ); + $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' ); + } }