From ff1ddb61babdc8bcee1b761fabcd8f24067f9a74 Mon Sep 17 00:00:00 2001 From: brookewp Date: Tue, 4 Mar 2025 17:45:21 -0800 Subject: [PATCH 1/3] Add collection/loop query to selection queries Signed-off-by: brookewp --- .../rest-api/art-institute/art-institute.php | 43 +++++++++++++++++++ inc/Editor/BlockManagement/ConfigRegistry.php | 3 +- inc/Validation/ConfigSchemas.php | 1 + .../components/InnerBlocks.tsx | 9 +++- .../placeholders/ItemSelectQueryType.tsx | 17 +++++++- 5 files changed, 69 insertions(+), 4 deletions(-) diff --git a/example/rest-api/art-institute/art-institute.php b/example/rest-api/art-institute/art-institute.php index d7e333ca..8650a6db 100644 --- a/example/rest-api/art-institute/art-institute.php +++ b/example/rest-api/art-institute/art-institute.php @@ -53,6 +53,7 @@ function register_aic_block(): void { 'name' => 'Art ID', 'type' => 'id', 'supports_bulk' => true, + 'required' => false, ], ], 'output_schema' => [ @@ -157,6 +158,44 @@ function register_aic_block(): void { ], ]); + $collection_query = HttpQuery::from_array([ + 'data_source' => $aic_data_source, + 'endpoint' => function ( array $input_variables ) use ( $aic_data_source ): string { + $endpoint = $aic_data_source->get_endpoint(); + return add_query_arg( [ + 'limit' => 10, + 'fields' => 'id,title,image_id,artist_title', + ], $endpoint ); + }, + 'output_schema' => [ + 'is_collection' => true, + 'path' => '$.data[*]', + 'type' => [ + 'id' => [ + 'name' => 'Art ID', + 'type' => 'id', + ], + 'artist_title' => [ + 'name' => 'Artist Title', + 'type' => 'string', + 'path' => '$.artist_title', + ], + 'title' => [ + 'name' => 'Title', + 'type' => 'string', + 'path' => '$.title', + ], + 'image_url' => [ + 'name' => 'Image URL', + 'generate' => function ( $data ): string { + return 'https://www.artic.edu/iiif/2/' . $data['image_id'] . '/full/843,/0/default.jpg'; + }, + 'type' => 'image_url', + ], + ], + ], + ]); + register_remote_data_block([ 'title' => 'Art Institute of Chicago', 'render_query' => [ @@ -167,6 +206,10 @@ function register_aic_block(): void { 'query' => $search_art_query, 'type' => 'search', ], + [ + 'query' => $collection_query, + 'type' => 'collection', + ], ], ]); } diff --git a/inc/Editor/BlockManagement/ConfigRegistry.php b/inc/Editor/BlockManagement/ConfigRegistry.php index 1ca0f474..84faac22 100644 --- a/inc/Editor/BlockManagement/ConfigRegistry.php +++ b/inc/Editor/BlockManagement/ConfigRegistry.php @@ -25,6 +25,7 @@ class ConfigRegistry { public const DISPLAY_QUERY_KEY = 'display'; public const LIST_QUERY_KEY = 'list'; public const SEARCH_QUERY_KEY = 'search'; + public const COLLECTION_QUERY_KEY = 'collection'; public static function init( ?LoggerInterface $logger = null ): void { self::$logger = $logger ?? LoggerManager::instance(); @@ -89,7 +90,7 @@ function ( $input ) { 'inputs' => array_map( function ( $slug, $input_var ) { return [ 'name' => $input_var['name'] ?? $slug, - 'required' => $input_var['required'] ?? true, + 'required' => $input_var['required'] ?? false, 'slug' => $slug, 'type' => $input_var['type'] ?? 'string', 'supports_bulk' => $input_var['supports_bulk'] ?? false, diff --git a/inc/Validation/ConfigSchemas.php b/inc/Validation/ConfigSchemas.php index 2f570c74..d5340f0e 100644 --- a/inc/Validation/ConfigSchemas.php +++ b/inc/Validation/ConfigSchemas.php @@ -90,6 +90,7 @@ private static function generate_remote_data_block_config_schema(): array { Types::serialized_config_for( HttpQueryInterface::class ), ), 'type' => Types::enum( + ConfigRegistry::COLLECTION_QUERY_KEY, ConfigRegistry::LIST_QUERY_KEY, ConfigRegistry::SEARCH_QUERY_KEY ), diff --git a/src/blocks/remote-data-container/components/InnerBlocks.tsx b/src/blocks/remote-data-container/components/InnerBlocks.tsx index 03acaaf7..90b4337b 100644 --- a/src/blocks/remote-data-container/components/InnerBlocks.tsx +++ b/src/blocks/remote-data-container/components/InnerBlocks.tsx @@ -11,12 +11,17 @@ interface InnerBlocksProps { export function InnerBlocks( props: InnerBlocksProps ) { const { - blockConfig: { loop }, + blockConfig: { loop, selectors }, getInnerBlocks, remoteData, } = props; - if ( loop || remoteData.results.length > 1 ) { + // Use loop template for both loop blocks and collections + if ( + loop || + remoteData.results.length > 1 || + selectors.some( selector => selector.type === 'collection' ) + ) { return ; } diff --git a/src/blocks/remote-data-container/components/placeholders/ItemSelectQueryType.tsx b/src/blocks/remote-data-container/components/placeholders/ItemSelectQueryType.tsx index 898f1bec..b1e77add 100644 --- a/src/blocks/remote-data-container/components/placeholders/ItemSelectQueryType.tsx +++ b/src/blocks/remote-data-container/components/placeholders/ItemSelectQueryType.tsx @@ -1,4 +1,4 @@ -import { ButtonGroup } from '@wordpress/components'; +import { Button, ButtonGroup } from '@wordpress/components'; import { InputModal } from '../modals/InputModal'; import { InputPopover } from '../popovers/InputPopover'; @@ -38,6 +38,21 @@ export function ItemSelectQueryType( props: ItemSelectQueryTypeProps ) { { ...selectorProps } /> ); + case 'collection': + return ( + + ); case 'input': return selector.inputs.length === 1 && selector.inputs[ 0 ] ? ( From 92f62d07d0d76f708cb5c6068bc97fba2248bf82 Mon Sep 17 00:00:00 2001 From: brookewp Date: Thu, 6 Mar 2025 12:15:05 -0800 Subject: [PATCH 2/3] Move loop/collection query to render query Signed-off-by: brookewp --- .../rest-api/art-institute/art-institute.php | 19 +- inc/Editor/BlockManagement/ConfigRegistry.php | 194 ++++++++++-------- inc/Validation/ConfigSchemas.php | 16 +- .../components/InnerBlocks.tsx | 6 +- 4 files changed, 142 insertions(+), 93 deletions(-) diff --git a/example/rest-api/art-institute/art-institute.php b/example/rest-api/art-institute/art-institute.php index 8650a6db..4e313340 100644 --- a/example/rest-api/art-institute/art-institute.php +++ b/example/rest-api/art-institute/art-institute.php @@ -32,18 +32,18 @@ function register_aic_block(): void { 'data_source' => $aic_data_source, 'endpoint' => function ( array $input_variables ) use ( $aic_data_source ): string { $endpoint = $aic_data_source->get_endpoint(); - + // Get and clean IDs from comma-separated string $ids = array_filter( array_map( 'trim', explode( ',', (string) $input_variables['id'] ) ), 'strlen' ); - - if ( !empty( $ids ) ) { + + if ( ! empty( $ids ) ) { return add_query_arg([ 'ids' => implode( ',', $ids ), 'fields' => 'id,title,image_id,artist_title', - ], $endpoint); + ], $endpoint ); } return $endpoint; @@ -200,16 +200,19 @@ function register_aic_block(): void { 'title' => 'Art Institute of Chicago', 'render_query' => [ 'query' => $get_art_query, + 'queries' => [ + 'collection' => [ + 'query' => $collection_query, + 'type' => 'collection', + 'display_name' => 'Collection', + ], + ], ], 'selection_queries' => [ [ 'query' => $search_art_query, 'type' => 'search', ], - [ - 'query' => $collection_query, - 'type' => 'collection', - ], ], ]); } diff --git a/inc/Editor/BlockManagement/ConfigRegistry.php b/inc/Editor/BlockManagement/ConfigRegistry.php index 84faac22..08fbbe46 100644 --- a/inc/Editor/BlockManagement/ConfigRegistry.php +++ b/inc/Editor/BlockManagement/ConfigRegistry.php @@ -72,105 +72,109 @@ function ( $input ) { $has_bulk_support = !empty( $bulk_supported_inputs ); - // Build the base configuration for the block. This is our own internal - // configuration, not what will be passed to WordPress's register_block_type. - // @see BlockRegistration::register_block_type::register_blocks. - $config = [ - 'description' => '', - 'name' => $block_name, - 'loop' => $user_config[ self::RENDER_QUERY_KEY ]['loop'] ?? false, - 'overrides' => $user_config['overrides'] ?? [], - 'patterns' => [], - 'queries' => [ + // Initialize queries array with display query as default + $queries = [ self::DISPLAY_QUERY_KEY => $display_query, - ], - 'selectors' => [ - [ - 'image_url' => $display_query->get_image_url(), - 'inputs' => array_map( function ( $slug, $input_var ) { - return [ - 'name' => $input_var['name'] ?? $slug, - 'required' => $input_var['required'] ?? false, - 'slug' => $slug, - 'type' => $input_var['type'] ?? 'string', - 'supports_bulk' => $input_var['supports_bulk'] ?? false, - ]; - }, array_keys( $input_schema ), array_values( $input_schema ) ), - 'name' => 'Manual input', - 'query_key' => self::DISPLAY_QUERY_KEY, - 'type' => 'input', + ]; + + // Process additional render queries if present + if ( isset( $user_config[ self::RENDER_QUERY_KEY ]['queries'] ) && is_array( $user_config[ self::RENDER_QUERY_KEY ]['queries'] ) ) { + foreach ( $user_config[ self::RENDER_QUERY_KEY ]['queries'] as $query_key => $query_config ) { + $queries[ $query_key ] = self::inflate_query( $query_config['query'] ); + } + } + + // Build the base configuration for the block. This is our own internal + // configuration, not what will be passed to WordPress's register_block_type. + // @see BlockRegistration::register_block_type::register_blocks. + $config = [ + 'description' => '', + 'name' => $block_name, + 'loop' => $user_config[ self::RENDER_QUERY_KEY ]['loop'] ?? false, + 'overrides' => $user_config['overrides'] ?? [], + 'patterns' => [], + 'queries' => $queries, + 'selectors' => [ + self::create_selector( $display_query, self::DISPLAY_QUERY_KEY, 'input', 'Manual input', $has_bulk_support ), ], - ], - 'title' => $block_title, - ]; + 'title' => $block_title, + ]; + + // Add collection queries to selectors if present + if ( isset( $user_config[ self::RENDER_QUERY_KEY ]['queries'] ) && is_array( $user_config[ self::RENDER_QUERY_KEY ]['queries'] ) ) { + foreach ( $user_config[ self::RENDER_QUERY_KEY ]['queries'] as $query_key => $query_config ) { + array_unshift( + $config['selectors'], + self::create_selector( + $queries[ $query_key ], + $query_key, + $query_config['type'], + $query_config['display_name'] ?? null, + $query_config['supports_bulk'] ?? false + ) + ); + } + } - // Register "selectors" which allow the user to use a query to assist in - // selecting data for display by the block. - foreach ( $user_config[ self::SELECTION_QUERIES_KEY ] ?? [] as $selection_query ) { - $from_query = self::inflate_query( $selection_query['query'] ); - $from_query_type = $selection_query['type']; - $to_query = $display_query; + // Register "selectors" which allow the user to use a query to assist in + // selecting data for display by the block. + foreach ( $user_config[ self::SELECTION_QUERIES_KEY ] ?? [] as $selection_query ) { + $from_query = self::inflate_query( $selection_query['query'] ); + $from_query_type = $selection_query['type']; + $to_query = $display_query; - $config['queries'][ $from_query::class ] = $from_query; + $config['queries'][ $from_query::class ] = $from_query; - $from_input_schema = $from_query->get_input_schema(); - $from_output_schema = $from_query->get_output_schema(); + $from_input_schema = $from_query->get_input_schema(); + $from_output_schema = $from_query->get_output_schema(); - foreach ( array_keys( $to_query->get_input_schema() ) as $to ) { - if ( ! isset( $from_output_schema['type'][ $to ] ) ) { - return self::create_error( $block_title, sprintf( 'Cannot map key "%1$s" from %2$s query. The display query for this block requires a "%1$s" key as an input, but it is not present in the output schema for the %2$s query. Try adding a "%1$s" mapping to the output schema for the %2$s query.', esc_html( $to ), $from_query_type ) ); + foreach ( array_keys( $to_query->get_input_schema() ) as $to ) { + if ( ! isset( $from_output_schema['type'][ $to ] ) ) { + return self::create_error( $block_title, sprintf( 'Cannot map key "%1$s" from %2$s query. The display query for this block requires a "%1$s" key as an input, but it is not present in the output schema for the %2$s query. Try adding a "%1$s" mapping to the output schema for the %2$s query.', esc_html( $to ), $from_query_type ) ); + } } - } - if ( self::SEARCH_QUERY_KEY === $from_query_type ) { - $search_input_count = count( array_filter( $from_input_schema, function ( array $input_var ): bool { - return 'ui:search_input' === $input_var['type']; - } ) ); + if ( self::SEARCH_QUERY_KEY === $from_query_type ) { + $search_input_count = count( array_filter( $from_input_schema, function ( array $input_var ): bool { + return 'ui:search_input' === $input_var['type']; + } ) ); - if ( 1 !== $search_input_count ) { - return self::create_error( $block_title, 'A search query must have one input variable with type "ui:search_input"' ); + if ( 1 !== $search_input_count ) { + return self::create_error( $block_title, 'A search query must have one input variable with type "ui:search_input"' ); + } } - } - // Add the selector to the configuration. - array_unshift( - $config['selectors'], - [ - 'image_url' => $from_query->get_image_url(), - 'inputs' => array_map( function ( $slug, $input_var ) { - return [ - 'name' => $input_var['name'] ?? $slug, - 'required' => $input_var['required'] ?? false, - 'slug' => $slug, - 'type' => $input_var['type'] ?? 'string', - ]; - }, array_keys( $from_input_schema ), array_values( $from_input_schema ) ), - 'name' => $selection_query['display_name'] ?? ucfirst( $from_query_type ), - 'query_key' => $from_query::class, - 'supports_bulk' => $has_bulk_support, - 'type' => $from_query_type, - ] - ); - } + // Add the selector to the configuration. + array_unshift( + $config['selectors'], + self::create_selector( + $from_query, + $from_query::class, + $from_query_type, + $selection_query['display_name'] ?? null, + $has_bulk_support + ) + ); + } - // Register patterns which can be used with the block. - foreach ( $user_config['patterns'] ?? [] as $pattern ) { - $parsed_blocks = parse_blocks( $pattern['html'] ); - $parsed_blocks = BlockPatterns::add_block_arg_to_bindings( $block_name, $parsed_blocks ); - $pattern_content = serialize_blocks( $parsed_blocks ); + // Register patterns which can be used with the block. + foreach ( $user_config['patterns'] ?? [] as $pattern ) { + $parsed_blocks = parse_blocks( $pattern['html'] ); + $parsed_blocks = BlockPatterns::add_block_arg_to_bindings( $block_name, $parsed_blocks ); + $pattern_content = serialize_blocks( $parsed_blocks ); - $pattern_name = self::register_block_pattern( $block_name, $pattern['title'], $pattern_content ); + $pattern_name = self::register_block_pattern( $block_name, $pattern['title'], $pattern_content ); - // If the pattern role is specified and recognized, add it to the block configuration. - $recognized_roles = [ 'inner_blocks' ]; - if ( isset( $pattern['role'] ) && in_array( $pattern['role'], $recognized_roles, true ) ) { - $config['patterns'][ $pattern['role'] ] = $pattern_name; + // If the pattern role is specified and recognized, add it to the block configuration. + $recognized_roles = [ 'inner_blocks' ]; + if ( isset( $pattern['role'] ) && in_array( $pattern['role'], $recognized_roles, true ) ) { + $config['patterns'][ $pattern['role'] ] = $pattern_name; + } } - } - ConfigStore::set_block_configuration( $block_name, $config ); + ConfigStore::set_block_configuration( $block_name, $config ); - return true; + return true; } private static function register_block_pattern( string $block_name, string $pattern_title, string $pattern_content ): string { @@ -206,4 +210,30 @@ private static function inflate_query( array|QueryInterface $config ): QueryInte return $config; } + + // Create a selector for a query + private static function create_selector( + QueryInterface $query, + string $query_key, + string $type, + ?string $display_name = null, + bool $supports_bulk = false + ): array { + return [ + 'image_url' => $query->get_image_url(), + 'inputs' => array_map( function ( $slug, $input_var ) { + return [ + 'name' => $input_var['name'] ?? $slug, + 'required' => $input_var['required'] ?? false, + 'slug' => $slug, + 'type' => $input_var['type'] ?? 'string', + 'supports_bulk' => $input_var['supports_bulk'] ?? false, + ]; + }, array_keys( $query->get_input_schema() ), array_values( $query->get_input_schema() ) ), + 'name' => $display_name ?? ucfirst( $type ), + 'query_key' => $query_key, + 'type' => $type, + 'supports_bulk' => $supports_bulk, + ]; + } } diff --git a/inc/Validation/ConfigSchemas.php b/inc/Validation/ConfigSchemas.php index d5340f0e..dd433ff3 100644 --- a/inc/Validation/ConfigSchemas.php +++ b/inc/Validation/ConfigSchemas.php @@ -79,6 +79,21 @@ private static function generate_remote_data_block_config_schema(): array { Types::instance_of( QueryInterface::class ), Types::serialized_config_for( HttpQueryInterface::class ), ), + 'queries' => Types::nullable( + Types::record( + Types::string(), + Types::object( [ + 'display_name' => Types::nullable( Types::string() ), + 'query' => Types::one_of( + Types::instance_of( QueryInterface::class ), + Types::serialized_config_for( HttpQueryInterface::class ), + ), + 'type' => Types::enum( + ConfigRegistry::COLLECTION_QUERY_KEY, + ), + ] ) + ) + ), 'loop' => Types::nullable( Types::boolean() ), ] ), 'selection_queries' => Types::nullable( @@ -90,7 +105,6 @@ private static function generate_remote_data_block_config_schema(): array { Types::serialized_config_for( HttpQueryInterface::class ), ), 'type' => Types::enum( - ConfigRegistry::COLLECTION_QUERY_KEY, ConfigRegistry::LIST_QUERY_KEY, ConfigRegistry::SEARCH_QUERY_KEY ), diff --git a/src/blocks/remote-data-container/components/InnerBlocks.tsx b/src/blocks/remote-data-container/components/InnerBlocks.tsx index 90b4337b..62f30077 100644 --- a/src/blocks/remote-data-container/components/InnerBlocks.tsx +++ b/src/blocks/remote-data-container/components/InnerBlocks.tsx @@ -5,7 +5,9 @@ import { LoopTemplate } from '@/blocks/remote-data-container/components/loop-tem interface InnerBlocksProps { blockConfig: BlockConfig; - getInnerBlocks: ( result: RemoteDataResult ) => BlockInstance< RemoteDataInnerBlockAttributes >[]; + getInnerBlocks: ( + result: RemoteDataApiResult + ) => BlockInstance< RemoteDataInnerBlockAttributes >[]; remoteData: RemoteData; } @@ -16,7 +18,7 @@ export function InnerBlocks( props: InnerBlocksProps ) { remoteData, } = props; - // Use loop template for both loop blocks and collections + // Use loop template for both loop blocks, multi-selection, or collection queries if ( loop || remoteData.results.length > 1 || From c645730797254903b417ae0e1ad64976903e661c Mon Sep 17 00:00:00 2001 From: brookewp Date: Thu, 6 Mar 2025 12:23:59 -0800 Subject: [PATCH 3/3] Renaming Signed-off-by: brookewp --- .../rest-api/art-institute/art-institute.php | 4 +- inc/Editor/BlockManagement/ConfigRegistry.php | 182 +++++++++--------- inc/Validation/ConfigSchemas.php | 5 +- 3 files changed, 99 insertions(+), 92 deletions(-) diff --git a/example/rest-api/art-institute/art-institute.php b/example/rest-api/art-institute/art-institute.php index 4e313340..c10e0b6d 100644 --- a/example/rest-api/art-institute/art-institute.php +++ b/example/rest-api/art-institute/art-institute.php @@ -200,8 +200,8 @@ function register_aic_block(): void { 'title' => 'Art Institute of Chicago', 'render_query' => [ 'query' => $get_art_query, - 'queries' => [ - 'collection' => [ + 'additional_queries' => [ + [ 'query' => $collection_query, 'type' => 'collection', 'display_name' => 'Collection', diff --git a/inc/Editor/BlockManagement/ConfigRegistry.php b/inc/Editor/BlockManagement/ConfigRegistry.php index 08fbbe46..899af5e6 100644 --- a/inc/Editor/BlockManagement/ConfigRegistry.php +++ b/inc/Editor/BlockManagement/ConfigRegistry.php @@ -49,7 +49,21 @@ public static function register_block( array $user_config = [] ): bool|WP_Error return self::create_error( $block_title, sprintf( 'Block %s has already been registered', $block_name ) ); } + // Process default render query first $display_query = self::inflate_query( $user_config[ self::RENDER_QUERY_KEY ]['query'] ); + + // Initialize queries array with display query as default + $queries = [ + self::DISPLAY_QUERY_KEY => $display_query, + ]; + + // Process additional render queries if present + if ( isset( $user_config[ self::RENDER_QUERY_KEY ]['additional_queries'] ) ) { + foreach ( $user_config[ self::RENDER_QUERY_KEY ]['additional_queries'] as $query_config ) { + $queries[ $query_config['type'] ] = self::inflate_query( $query_config['query'] ); + } + } + $input_schema = $display_query->get_input_schema(); // Check if render query has any bulk-supporting inputs @@ -72,109 +86,103 @@ function ( $input ) { $has_bulk_support = !empty( $bulk_supported_inputs ); - // Initialize queries array with display query as default - $queries = [ - self::DISPLAY_QUERY_KEY => $display_query, - ]; - - // Process additional render queries if present - if ( isset( $user_config[ self::RENDER_QUERY_KEY ]['queries'] ) && is_array( $user_config[ self::RENDER_QUERY_KEY ]['queries'] ) ) { - foreach ( $user_config[ self::RENDER_QUERY_KEY ]['queries'] as $query_key => $query_config ) { - $queries[ $query_key ] = self::inflate_query( $query_config['query'] ); - } - } - - // Build the base configuration for the block. This is our own internal - // configuration, not what will be passed to WordPress's register_block_type. - // @see BlockRegistration::register_block_type::register_blocks. - $config = [ - 'description' => '', - 'name' => $block_name, - 'loop' => $user_config[ self::RENDER_QUERY_KEY ]['loop'] ?? false, - 'overrides' => $user_config['overrides'] ?? [], - 'patterns' => [], - 'queries' => $queries, - 'selectors' => [ - self::create_selector( $display_query, self::DISPLAY_QUERY_KEY, 'input', 'Manual input', $has_bulk_support ), - ], - 'title' => $block_title, - ]; - - // Add collection queries to selectors if present - if ( isset( $user_config[ self::RENDER_QUERY_KEY ]['queries'] ) && is_array( $user_config[ self::RENDER_QUERY_KEY ]['queries'] ) ) { - foreach ( $user_config[ self::RENDER_QUERY_KEY ]['queries'] as $query_key => $query_config ) { - array_unshift( - $config['selectors'], - self::create_selector( - $queries[ $query_key ], - $query_key, - $query_config['type'], - $query_config['display_name'] ?? null, - $query_config['supports_bulk'] ?? false - ) - ); - } + // Build the base configuration for the block. This is our own internal + // configuration, not what will be passed to WordPress's register_block_type. + // @see BlockRegistration::register_block_type::register_blocks. + $config = [ + 'description' => '', + 'name' => $block_name, + 'loop' => $user_config[ self::RENDER_QUERY_KEY ]['loop'] ?? false, + 'overrides' => $user_config['overrides'] ?? [], + 'patterns' => [], + 'queries' => $queries, + 'selectors' => [ + self::create_selector( + $display_query, + self::DISPLAY_QUERY_KEY, + 'input', + 'Manual input', + $has_bulk_support + ), + ], + 'title' => $block_title, + ]; + + // Add collection queries to selectors if present + if ( isset( $user_config[ self::RENDER_QUERY_KEY ]['additional_queries'] ) ) { + foreach ( $user_config[ self::RENDER_QUERY_KEY ]['additional_queries'] as $query_config ) { + array_unshift( + $config['selectors'], + self::create_selector( + $queries[ $query_config['type'] ], + $query_config['type'], + $query_config['type'], + $query_config['display_name'] ?? null, + $has_bulk_support + ) + ); } + } - // Register "selectors" which allow the user to use a query to assist in - // selecting data for display by the block. - foreach ( $user_config[ self::SELECTION_QUERIES_KEY ] ?? [] as $selection_query ) { - $from_query = self::inflate_query( $selection_query['query'] ); - $from_query_type = $selection_query['type']; - $to_query = $display_query; + // Register "selectors" which allow the user to use a query to assist in + // selecting data for display by the block. + foreach ( $user_config[ self::SELECTION_QUERIES_KEY ] ?? [] as $selection_query ) { + $from_query = self::inflate_query( $selection_query['query'] ); + $from_query_type = $selection_query['type']; + $to_query = $display_query; - $config['queries'][ $from_query::class ] = $from_query; + $config['queries'][ $from_query::class ] = $from_query; - $from_input_schema = $from_query->get_input_schema(); - $from_output_schema = $from_query->get_output_schema(); + $from_input_schema = $from_query->get_input_schema(); + $from_output_schema = $from_query->get_output_schema(); - foreach ( array_keys( $to_query->get_input_schema() ) as $to ) { - if ( ! isset( $from_output_schema['type'][ $to ] ) ) { - return self::create_error( $block_title, sprintf( 'Cannot map key "%1$s" from %2$s query. The display query for this block requires a "%1$s" key as an input, but it is not present in the output schema for the %2$s query. Try adding a "%1$s" mapping to the output schema for the %2$s query.', esc_html( $to ), $from_query_type ) ); - } + foreach ( array_keys( $to_query->get_input_schema() ) as $to ) { + if ( ! isset( $from_output_schema['type'][ $to ] ) ) { + return self::create_error( $block_title, sprintf( 'Cannot map key "%1$s" from %2$s query. The display query for this block requires a "%1$s" key as an input, but it is not present in the output schema for the %2$s query. Try adding a "%1$s" mapping to the output schema for the %2$s query.', esc_html( $to ), $from_query_type ) ); } + } - if ( self::SEARCH_QUERY_KEY === $from_query_type ) { - $search_input_count = count( array_filter( $from_input_schema, function ( array $input_var ): bool { - return 'ui:search_input' === $input_var['type']; - } ) ); + if ( self::SEARCH_QUERY_KEY === $from_query_type ) { + $search_input_count = count( array_filter( $from_input_schema, function ( array $input_var ): bool { + return 'ui:search_input' === $input_var['type']; + } ) ); - if ( 1 !== $search_input_count ) { - return self::create_error( $block_title, 'A search query must have one input variable with type "ui:search_input"' ); - } + if ( 1 !== $search_input_count ) { + return self::create_error( $block_title, 'A search query must have one input variable with type "ui:search_input"' ); } - - // Add the selector to the configuration. - array_unshift( - $config['selectors'], - self::create_selector( - $from_query, - $from_query::class, - $from_query_type, - $selection_query['display_name'] ?? null, - $has_bulk_support - ) - ); } - // Register patterns which can be used with the block. - foreach ( $user_config['patterns'] ?? [] as $pattern ) { - $parsed_blocks = parse_blocks( $pattern['html'] ); - $parsed_blocks = BlockPatterns::add_block_arg_to_bindings( $block_name, $parsed_blocks ); - $pattern_content = serialize_blocks( $parsed_blocks ); + // Add the selector to the configuration. + array_unshift( + $config['selectors'], + self::create_selector( + $from_query, + $from_query::class, + $from_query_type, + $selection_query['display_name'] ?? null, + $has_bulk_support + ) + ); + } - $pattern_name = self::register_block_pattern( $block_name, $pattern['title'], $pattern_content ); + // Register patterns which can be used with the block. + foreach ( $user_config['patterns'] ?? [] as $pattern ) { + $parsed_blocks = parse_blocks( $pattern['html'] ); + $parsed_blocks = BlockPatterns::add_block_arg_to_bindings( $block_name, $parsed_blocks ); + $pattern_content = serialize_blocks( $parsed_blocks ); - // If the pattern role is specified and recognized, add it to the block configuration. - $recognized_roles = [ 'inner_blocks' ]; - if ( isset( $pattern['role'] ) && in_array( $pattern['role'], $recognized_roles, true ) ) { - $config['patterns'][ $pattern['role'] ] = $pattern_name; - } + $pattern_name = self::register_block_pattern( $block_name, $pattern['title'], $pattern_content ); + + // If the pattern role is specified and recognized, add it to the block configuration. + $recognized_roles = [ 'inner_blocks' ]; + if ( isset( $pattern['role'] ) && in_array( $pattern['role'], $recognized_roles, true ) ) { + $config['patterns'][ $pattern['role'] ] = $pattern_name; } + } - ConfigStore::set_block_configuration( $block_name, $config ); + ConfigStore::set_block_configuration( $block_name, $config ); - return true; + return true; } private static function register_block_pattern( string $block_name, string $pattern_title, string $pattern_content ): string { diff --git a/inc/Validation/ConfigSchemas.php b/inc/Validation/ConfigSchemas.php index dd433ff3..9cdc09df 100644 --- a/inc/Validation/ConfigSchemas.php +++ b/inc/Validation/ConfigSchemas.php @@ -79,9 +79,8 @@ private static function generate_remote_data_block_config_schema(): array { Types::instance_of( QueryInterface::class ), Types::serialized_config_for( HttpQueryInterface::class ), ), - 'queries' => Types::nullable( - Types::record( - Types::string(), + 'additional_queries' => Types::nullable( + Types::list_of( Types::object( [ 'display_name' => Types::nullable( Types::string() ), 'query' => Types::one_of(