Skip to content

Add multiple queries to single block #405

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
51 changes: 45 additions & 6 deletions example/rest-api/art-institute/art-institute.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,55 @@
],
]);

$collection_query = HttpQuery::from_array([
'data_source' => $aic_data_source,
'endpoint' => function ( array $input_variables ) use ( $aic_data_source ): string {

Check failure on line 161 in example/rest-api/art-institute/art-institute.php

View workflow job for this annotation

GitHub Actions / Psalm

UnusedClosureParam

example/rest-api/art-institute/art-institute.php:161:34: UnusedClosureParam: Param input_variables is never referenced in this method (see https://psalm.dev/188)
$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',
'icon' => 'art',
'render_query' => [
'query' => $get_art_query,
'queries' => [
'display' => $get_art_query,
'collection' => $collection_query,
'search' => $search_art_query,
],
'selection_queries' => [
[
'query' => $search_art_query,
'type' => 'search',
'query_configurations' => [
'display' => [
'source_query' => 'search',
],
],
]);
Expand Down
167 changes: 105 additions & 62 deletions inc/Editor/BlockManagement/ConfigRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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();
Expand All @@ -48,33 +49,37 @@
return self::create_error( $block_title, sprintf( 'Block %s has already been registered', $block_name ) );
}

$display_query = self::inflate_query( $user_config[ self::RENDER_QUERY_KEY ]['query'] );
$input_schema = $display_query->get_input_schema();
// Get the display query (always use 'display' key)
if (!isset($user_config['queries']['display'])) {
return self::create_error($block_title, 'No display query found in queries (must use key "display")');
}
$display_query = self::inflate_query($user_config['queries']['display']);

// Initialize queries array with display query
$queries = [
self::DISPLAY_QUERY_KEY => $display_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.
// Build the base configuration
$config = [
'description' => '',
'icon' => $user_config['icon'] ?? 'cloud',
'name' => $block_name,
'loop' => $user_config[ self::RENDER_QUERY_KEY ]['loop'] ?? false,
'loop' => $user_config['loop'] ?? false,
'overrides' => $user_config['overrides'] ?? [],
'patterns' => [],
'queries' => [
self::DISPLAY_QUERY_KEY => $display_query,
],
'queries' => $queries,
'selectors' => [
[
'image_url' => $display_query->get_image_url(),
'inputs' => array_map( function ( $slug, $input_var ) {
'inputs' => array_map(function ($slug, $input_var) {
return [
'name' => $input_var['name'] ?? $slug,
'required' => $input_var['required'] ?? true,
'slug' => $slug,
'type' => $input_var['type'] ?? 'string',
];
}, array_keys( $input_schema ), array_values( $input_schema ) ),
}, array_keys($display_query->get_input_schema()), array_values($display_query->get_input_schema())),
'name' => 'Manual input',
'query_key' => self::DISPLAY_QUERY_KEY,
'type' => 'input',
Expand All @@ -83,71 +88,82 @@
'title' => $block_title,
];

// 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;

$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 ) );
}
// Add other queries and create selectors based on query_configurations
foreach ($user_config['queries'] as $key => $query) {
if ($key === 'display') {
continue;
}

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"' );
$query = self::inflate_query($query);
$queries[$key] = $query;

// Check if this query is configured as a source for another query
$is_source_query = false;
foreach ($user_config['query_configurations'] ?? [] as $target_key => $target_config) {

Check failure on line 102 in inc/Editor/BlockManagement/ConfigRegistry.php

View workflow job for this annotation

GitHub Actions / Psalm

UnusedVariable

inc/Editor/BlockManagement/ConfigRegistry.php:102:59: UnusedVariable: $target_key is never referenced or the value is not used (see https://psalm.dev/077)
if ($target_config['source_query'] === $key) {
$is_source_query = true;
array_unshift(
$config['selectors'],
[
'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',
];
}, array_keys($query->get_input_schema()), array_values($query->get_input_schema())),
'name' => ucfirst($key),
'query_key' => $key,
'type' => 'search',
]
);
break;
}
}

// 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,
'type' => $from_query_type,
]
);
// If not a source query and it's a collection query, add it as collection type
if (!$is_source_query && $key === 'collection') {
array_unshift(
$config['selectors'],
[
'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',
];
}, array_keys($query->get_input_schema()), array_values($query->get_input_schema())),
'name' => 'Collection',
'query_key' => $key,
'type' => 'collection',
]
);
}
}

// Set the queries on the config
$config['queries'] = $queries;

// 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 );
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;
$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;
}

Expand Down Expand Up @@ -184,4 +200,31 @@

return $config;
}

// Create a selector for a query
private static function create_selector(

Check failure on line 205 in inc/Editor/BlockManagement/ConfigRegistry.php

View workflow job for this annotation

GitHub Actions / Psalm

UnusedMethod

inc/Editor/BlockManagement/ConfigRegistry.php:205:26: UnusedMethod: Cannot find any calls to private method RemoteDataBlocks\Editor\BlockManagement\ConfigRegistry::create_selector (see https://psalm.dev/076)
QueryInterface $query,
string $query_key,
string $type,
?string $display_name = null
): array {
$input_schema = $query->get_input_schema();

// Convert object input schema to array format
$inputs = is_array($input_schema) ? array_map(
function($key, $schema) {
return array_merge(['slug' => $key], $schema);
},
array_keys($input_schema),
array_values($input_schema)
) : [];

return [
'query_key' => $query_key,
'type' => $type,
'name' => $display_name ?? ucfirst($type),
'inputs' => $inputs,
'image_url' => null,
];
}
}
16 changes: 8 additions & 8 deletions inc/ExampleApi/ExampleApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,17 @@ public static function register_remote_data_block(): void {
'query_runner' => new ExampleApiQueryRunner(),
] );

register_remote_data_block( [
register_remote_data_block([
'title' => self::$block_title,
'render_query' => [
'query' => $get_record_query,
'queries' => [
'display' => $get_record_query,
'list' => $get_table_query,
],
'selection_queries' => [
[
'query' => $get_table_query,
'type' => 'list',
'query_configurations' => [
'display' => [
'source_query' => 'list',
],
],
] );
]);
}
}
44 changes: 22 additions & 22 deletions inc/Integrations/Airtable/AirtableIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,27 @@ public static function register_blocks_for_airtable_data_source(
$tables = $data_source->to_array()['service_config']['tables'];

foreach ( $tables as $table ) {
$query = self::get_query( $data_source, $table );
$list_query = self::get_list_query( $data_source, $table );

register_remote_data_block(
array_merge(
[
'title' => $data_source->get_display_name() . '/' . $table['name'],
'icon' => 'editor-table',
'render_query' => [
'query' => $query,
],
'selection_queries' => [
[
'query' => $list_query,
'type' => 'list',
],
$query = self::get_query( $data_source, $table );
$list_query = self::get_list_query( $data_source, $table );

register_remote_data_block(
array_merge(
[
'title' => $data_source->get_display_name() . '/' . $table['name'],
'icon' => 'editor-table',
'queries' => [
'display' => $query,
'list' => $list_query,
],
'query_configurations' => [
'display' => [
'source_query' => 'list',
],
],
$block_overrides
)
);
],
$block_overrides
)
);
}
}

Expand All @@ -71,10 +71,10 @@ public static function register_loop_blocks_for_airtable_data_source(
array_merge(
[
'title' => sprintf( '%s/%s Loop', $data_source->get_display_name(), $table['name'] ),
'render_query' => [
'loop' => true,
'query' => $list_query,
'queries' => [
'display' => $list_query,
],
'loop' => true,
],
$block_overrides
)
Expand Down
Loading
Loading