Skip to content
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

Fix "Array" values in GraphQL block array data #61

Merged
merged 8 commits into from
May 9, 2024
41 changes: 32 additions & 9 deletions src/graphql/graphql-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public static function init() {
}

/**
* Extract the blocks data for a post, and return back in the format expected by the graphQL API.
* Extract the blocks data for a post, and return back in the format expected by the GraphQL API.
*
* @param \WPGraphQL\Model\Post $post_model Post model for post.
*
Expand Down Expand Up @@ -91,12 +91,7 @@ public static function map_attributes( $block ) {
unset( $block['attributes'] );
} elseif ( isset( $block['attributes'] ) && ! empty( $block['attributes'] ) ) {
$block['attributes'] = array_map(
function ( $name, $value ) {
return [
'name' => $name,
'value' => strval( $value ),
];
},
[ __CLASS__, 'get_block_attribute_pair' ],
array_keys( $block['attributes'] ),
array_values( $block['attributes'] )
);
Expand Down Expand Up @@ -159,14 +154,18 @@ public static function register_types() {
[
'description' => 'Block attribute',
'fields' => [
'name' => [
'name' => [
'type' => [ 'non_null' => 'String' ],
'description' => 'Block data attribute name',
],
'value' => [
'value' => [
'type' => [ 'non_null' => 'String' ],
'description' => 'Block data attribute value',
],
'isValueJsonEncoded' => [
'type' => [ 'non_null' => 'Boolean' ],
'description' => 'True if value is a complex JSON-encoded field. This is used to encode attribute types like arrays and objects.',
],
],
],
);
Expand Down Expand Up @@ -252,6 +251,30 @@ public static function register_types() {
]
);
}

/**
* Given a block attribute name and value, return a BlockAttribute array.
*
* @param string $name The name of the block attribute.
* @param mixed $value The value of the block attribute.
*
* @return array
*/
public static function get_block_attribute_pair( $name, $value ) {
// Unknown array types (table cells, for example) are encoded as JSON strings.
$is_value_json_encoded = false;

if ( ! is_scalar( $value ) ) {
$value = wp_json_encode( $value );
$is_value_json_encoded = true;
}

return [
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm thinking if it would make sense to specify the actual type of $value (which would be the best approach imo), or at least specifying that the content is encoded (we may run into performance issues by trying to decode every attribute in frontend)

'name' => $name,
'value' => strval( $value ),
'isValueJsonEncoded' => $is_value_json_encoded,
];
}
}

GraphQLApi::init();
136 changes: 110 additions & 26 deletions tests/graphql/test-graphql-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,41 @@ public function test_is_graphql_enabled_false() {
remove_filter( 'vip_block_data_api__is_graphql_enabled', $is_graphql_enabled_function, 10, 0 );
}

// get_blocks_data() tests

public function test_get_blocks_data() {
$html = '
<!-- wp:paragraph -->
<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><!-- wp:paragraph -->
<p>This is a heading inside a quote</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote"><!-- wp:paragraph -->
<p>This is a heading inside a quote</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><!-- wp:heading -->
<h2 class="wp-block-heading">This is a heading</h2>
<!-- /wp:heading --></blockquote>
<!-- /wp:quote --></blockquote>
<!-- /wp:quote -->
';
<!-- wp:quote -->
<blockquote class="wp-block-quote"><!-- wp:heading -->
<h2 class="wp-block-heading">This is a heading</h2>
<!-- /wp:heading --></blockquote>
<!-- /wp:quote --></blockquote>
<!-- /wp:quote -->
';

$expected_blocks = [
'blocks' => [
[
'name' => 'core/paragraph',
'attributes' => [
[
'name' => 'content',
'value' => 'Welcome to WordPress. This is your first post. Edit or delete it, then start writing!',
'name' => 'content',
'value' => 'Welcome to WordPress. This is your first post. Edit or delete it, then start writing!',
'isValueJsonEncoded' => false,
],
[
'name' => 'dropCap',
'value' => '',
'name' => 'dropCap',
'value' => '',
'isValueJsonEncoded' => false,
],
],
'id' => '1',
Expand All @@ -64,21 +68,24 @@ public function test_get_blocks_data() {
'name' => 'core/quote',
'attributes' => [
[
'name' => 'value',
'value' => '',
'name' => 'value',
'value' => '',
'isValueJsonEncoded' => false,
],
],
'innerBlocks' => [
[
'name' => 'core/paragraph',
'attributes' => [
[
'name' => 'content',
'value' => 'This is a heading inside a quote',
'name' => 'content',
'value' => 'This is a heading inside a quote',
'isValueJsonEncoded' => false,
],
[
'name' => 'dropCap',
'value' => '',
'name' => 'dropCap',
'value' => '',
'isValueJsonEncoded' => false,
],
],
'id' => '3',
Expand All @@ -87,8 +94,9 @@ public function test_get_blocks_data() {
'name' => 'core/quote',
'attributes' => [
[
'name' => 'value',
'value' => '',
'name' => 'value',
'value' => '',
'isValueJsonEncoded' => false,
],
],
'innerBlocks' => [
Expand All @@ -98,10 +106,12 @@ public function test_get_blocks_data() {
[
'name' => 'content',
'value' => 'This is a heading',
'isValueJsonEncoded' => false,
],
[
'name' => 'level',
'value' => '2',
'isValueJsonEncoded' => false,
],
],
'id' => '5',
Expand All @@ -124,6 +134,80 @@ public function test_get_blocks_data() {
$this->assertEquals( $expected_blocks, $blocks_data );
}

public function test_array_data_in_attribute() {
$html = '
<!-- wp:table -->
<figure class="wp-block-table">
<table>
<thead>
<tr>
<th>Header A</th>
<th>Header B</th>
</tr>
</thead>
<tbody>
<tr>
<td>Value A</td>
<td>Value B</td>
</tr>
<tr>
<td>Value C</td>
<td>Value D</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Footer A</td>
<td>Footer B</td>
</tr>
</tfoot>
</table>
</figure>
<!-- /wp:table -->
';

$expected_blocks = [
'blocks' => [
[
'name' => 'core/table',
'attributes' => [
[
'name' => 'hasFixedLayout',
'value' => false,
'isValueJsonEncoded' => false,
],
[
'name' => 'head',
'value' => '[{"cells":[{"content":"Header A","tag":"th"},{"content":"Header B","tag":"th"}]}]',
'isValueJsonEncoded' => true,
],
[
'name' => 'body',
'value' => '[{"cells":[{"content":"Value A","tag":"td"},{"content":"Value B","tag":"td"}]},{"cells":[{"content":"Value C","tag":"td"},{"content":"Value D","tag":"td"}]}]',
'isValueJsonEncoded' => true,
],
[
'name' => 'foot',
'value' => '[{"cells":[{"content":"Footer A","tag":"td"},{"content":"Footer B","tag":"td"}]}]',
'isValueJsonEncoded' => true,
],
],
'id' => '6',
],
],
];

$post = $this->factory()->post->create_and_get( [
'post_content' => $html,
] );

$blocks_data = GraphQLApi::get_blocks_data( $post );

$this->assertEquals( $expected_blocks, $blocks_data );
}

// flatten_inner_blocks() tests

public function test_flatten_inner_blocks() {
$inner_blocks = [
[
Expand Down
Loading