From 2e1ffda3fd3ae8cacc1361dc15cb272f304c7a93 Mon Sep 17 00:00:00 2001 From: Jason Bahl Date: Wed, 13 Jul 2022 13:43:56 -0600 Subject: [PATCH 1/2] - update get_acf_field_value() to use the value if the $root is an array that has a key matching the acf field name - update get_acf_field_value() to resolve fields from blocks using acf_setup_meta / acf_reset_meta - update docblock / spacing for register_graphql_field - update resolver for relationship field to compensate for root data coming in different shapes - update resolver for image/file fields to use loader and compensate for root data in different shapes - update resolver for gallery field to compensate for root data passed in different shapes - update resolver for user field to use the deferred loader - update get_all_graphql_types function to include blocks as locations that a field group can be assigned --- src/class-config.php | 178 +++++++++++++++++++++++++++---------------- 1 file changed, 111 insertions(+), 67 deletions(-) diff --git a/src/class-config.php b/src/class-config.php index 927f2b3..319bb94 100644 --- a/src/class-config.php +++ b/src/class-config.php @@ -354,7 +354,7 @@ public static function camel_case( $str, array $no_strip = [] ) { $str = ucwords( $str ); // Replace spaces $str = str_replace( ' ', '', $str ); - // Lowecase first letter + // Lowercase first letter $str = lcfirst( $str ); return $str; @@ -378,6 +378,23 @@ protected function get_acf_field_value( $root, $acf_field, $format = false ) { $id = $root['node']->ID; } + if ( is_array( $root ) && isset( $root[ $acf_field['name'] ] ) ) { + return $root[ $acf_field['name'] ]; + } + + // If the field is on a block, try and resolve from the block fields + if ( is_array( $root ) && isset( $root['blockName'], $root['attrs']['data'][ $acf_field['name'] ] ) && is_array( $root ) ) { + + // @see: https://support.advancedcustomfields.com/forums/topic/getting-get_field-outside-block-loop/#post-84022 + $block_id = isset( $root['attrs']['id'] ) ? $root['attrs']['id'] : null; + acf_setup_meta( $root['attrs']['data'], $block_id, true ); + $fields = get_fields(); + acf_reset_meta( $block_id ); + + return $fields[ $acf_field['name'] ] ?? null; + + } + if ( is_array( $root ) && ! ( ! empty( $root['type'] ) && 'options_page' === $root['type'] ) ) { if ( isset( $root[ $acf_field['key'] ] ) ) { @@ -458,6 +475,7 @@ protected function get_acf_field_value( $root, $acf_field, $format = false ) { } + /** * Filters the returned ACF field value * @@ -526,15 +544,16 @@ public static function get_supported_fields() { /** * Undocumented function * - * @param string $type_name The name of the GraphQL Type to add the field to. + * @param string $type_name The name of the GraphQL Type to add the field to. * @param string $field_name The name of the field to add to the GraphQL Type. - * @param array $config The GraphQL configuration of the field. + * @param array $config The GraphQL configuration of the field. * * @return mixed + * @throws Exception */ protected function register_graphql_field( string $type_name, string $field_name, array $config ) { - $acf_field = isset( $config['acf_field'] ) ? $config['acf_field'] : null; - $acf_type = isset( $acf_field['type'] ) ? $acf_field['type'] : null; + $acf_field = $config['acf_field'] ?? null; + $acf_type = $acf_field['type'] ?? null; if ( empty( $acf_type ) ) { return false; @@ -648,7 +667,7 @@ protected function register_graphql_field( string $type_name, string $field_name $field_type_name = $type_name . '_' . ucfirst( self::camel_case( $acf_field['name'] ) ); - if ( $this->type_registry->get_type( $field_type_name ) == $field_type_name ) { + if ( $this->type_registry->get_type( $field_type_name ) === $field_type_name ) { $type = $field_type_name; } else { $type_names = []; @@ -685,7 +704,16 @@ protected function register_graphql_field( string $type_name, string $field_name $value = $this->get_acf_field_value( $root, $acf_field ); if ( ! empty( $value ) && is_array( $value ) ) { - foreach ( $value as $post_id ) { + foreach ( $value as $post ) { + + $post_id = 0; + + if ( absint( $post ) ) { + $post_id = $post; + } else if ( $post instanceof \WP_Post ) { + $post_id = $post->ID; + } + $post_object = get_post( $post_id ); if ( $post_object instanceof \WP_Post ) { $post_model = new Post( $post_object ); @@ -704,7 +732,7 @@ protected function register_graphql_field( string $type_name, string $field_name if ( isset( $acf_field['post_type'] ) && is_array( $acf_field['post_type'] ) ) { $field_type_name = $type_name . '_' . ucfirst( self::camel_case( $acf_field['name'] ) ); - if ( $this->type_registry->get_type( $field_type_name ) == $field_type_name ) { + if ( $this->type_registry->get_type( $field_type_name ) === $field_type_name ) { $type = $field_type_name; } else { $type_names = []; @@ -797,7 +825,15 @@ protected function register_graphql_field( string $type_name, string $field_name 'resolve' => function( $root, $args, $context, $info ) use ( $acf_field ) { $value = $this->get_acf_field_value( $root, $acf_field ); - return DataSource::resolve_post_object( (int) $value, $context ); + if ( isset( $value->ID ) ) { + $id = $value->ID; + } elseif ( isset( $value['ID'] ) ) { + $id = $value['ID']; + } else { + $id = $value; + } + + return $context->get_loader( 'post' )->load_deferred( $id ); }, ]; break; @@ -819,7 +855,16 @@ protected function register_graphql_field( string $type_name, string $field_name $gallery = []; if ( ! empty( $value ) && is_array( $value ) ) { foreach ( $value as $image ) { - $post_object = get_post( (int) $image ); + + if ( isset( $image->ID ) ) { + $id = $image->ID; + } elseif ( isset( $image['ID'] ) ) { + $id = $image['ID']; + } else { + $id = $image; + } + + $post_object = get_post( (int) $id ); if ( $post_object instanceof \WP_Post ) { $post_model = new Post( $post_object ); $gallery[] = $post_model; @@ -847,31 +892,23 @@ protected function register_graphql_field( string $type_name, string $field_name $return = []; if ( ! empty( $value ) ) { if ( is_array( $value ) ) { - foreach ( $value as $id ) { - $user = get_user_by( 'id', $id ); - if ( ! empty( $user ) ) { - $user = new User( $user ); - if ( 'private' !== $user->get_visibility() ) { - $return[] = $user; - } - } - } + return $context->get_loader( 'user' )->load_deferred( $value ); + } + + if ( $value instanceof \WP_User ) { + $id = $value->ID; } else { - $user = get_user_by( 'id', absint( $value ) ); - if ( ! empty( $user ) ) { - $user = new User( $user ); - if ( 'private' !== $user->get_visibility() ) { - $return[] = $user; - } - } + $id = absint( $value ); } + + return $context->get_loader( 'user' )->load_deferred( $id ); } // If the field is allowed to be a multi select if ( 0 !== $acf_field['multiple'] ) { $return = ! empty( $return ) ? $return : null; } else { - $return = ! empty( $return[0] ) ? $return[0] : null; + $return = $return[0] ?? null; } return $return; @@ -946,7 +983,7 @@ protected function register_graphql_field( string $type_name, string $field_name case 'google_map': $field_type_name = 'ACF_GoogleMap'; - if ( $this->type_registry->get_type( $field_type_name ) == $field_type_name ) { + if ( $this->type_registry->get_type( $field_type_name ) === $field_type_name ) { $field_config['type'] = $field_type_name; break; } @@ -956,21 +993,21 @@ protected function register_graphql_field( string $type_name, string $field_name 'type' => 'String', 'description' => __( 'The street address associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['address'] ) ? $root['address'] : null; + return $root['address'] ?? null; }, ], 'latitude' => [ 'type' => 'Float', 'description' => __( 'The latitude associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['lat'] ) ? $root['lat'] : null; + return $root['lat'] ?? null; }, ], 'longitude' => [ 'type' => 'Float', 'description' => __( 'The longitude associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['lng'] ) ? $root['lng'] : null; + return $root['lng'] ?? null; }, ], ]; @@ -983,70 +1020,70 @@ protected function register_graphql_field( string $type_name, string $field_name 'type' => 'String', 'description' => __( 'The street name associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['street_name'] ) ? $root['street_name'] : null; + return $root['street_name'] ?? null; }, ], 'streetNumber' => [ 'type' => 'String', 'description' => __( 'The street number associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['street_number'] ) ? $root['street_number'] : null; + return $root['street_number'] ?? null; }, ], 'city' => [ 'type' => 'String', 'description' => __( 'The city associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['city'] ) ? $root['city'] : null; + return $root['city'] ?? null; }, ], 'state' => [ 'type' => 'String', 'description' => __( 'The state associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['state'] ) ? $root['state'] : null; + return $root['state'] ?? null; }, ], 'stateShort' => [ 'type' => 'String', 'description' => __( 'The state abbreviation associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['state_short'] ) ? $root['state_short'] : null; + return $root['state_short'] ?? null; }, ], 'postCode' => [ 'type' => 'String', 'description' => __( 'The post code associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['post_code'] ) ? $root['post_code'] : null; + return $root['post_code'] ?? null; }, ], 'country' => [ 'type' => 'String', 'description' => __( 'The country associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['country'] ) ? $root['country'] : null; + return $root['country'] ?? null; }, ], 'countryShort' => [ 'type' => 'String', 'description' => __( 'The country abbreviation associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['country_short'] ) ? $root['country_short'] : null; + return $root['country_short'] ?? null; }, ], 'placeId' => [ 'type' => 'String', 'description' => __( 'The country associated with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['place_id'] ) ? $root['place_id'] : null; + return $root['place_id'] ?? null; }, ], 'zoom' => [ 'type' => 'String', 'description' => __( 'The zoom defined with the map', 'wp-graphql-acf' ), 'resolve' => function( $root ) { - return isset( $root['zoom'] ) ? $root['zoom'] : null; + return $root['zoom'] ?? null; }, ], ]; @@ -1080,12 +1117,7 @@ protected function register_graphql_field( string $type_name, string $field_name return ! empty( $acf_field['name'] ) ? $acf_field['name'] : null; }, ], - ], - 'resolve' => function( $source ) use ( $acf_field ) { - $repeater = $this->get_acf_field_value( $source, $acf_field ); - - return ! empty( $repeater ) ? $repeater : []; - }, + ] ] ); @@ -1165,7 +1197,7 @@ protected function register_graphql_field( string $type_name, string $field_name $layout['parent'] = $acf_field; - $layout['show_in_graphql'] = isset( $acf_field['show_in_graphql'] ) ? (bool) $acf_field['show_in_graphql'] : true; + $layout['show_in_graphql'] = ! isset( $acf_field['show_in_graphql'] ) || (bool) $acf_field['show_in_graphql']; $this->add_field_group_fields( $layout, $flex_field_layout_name, true ); } } @@ -1211,7 +1243,7 @@ protected function add_field_group_fields( array $field_group, string $type_name * If the field group has the show_in_graphql setting configured, respect it's setting * otherwise default to true (for nested fields) */ - $field_group['show_in_graphql'] = isset( $field_group['show_in_graphql'] ) ? (boolean) $field_group['show_in_graphql'] : true; + $field_group['show_in_graphql'] = ! isset( $field_group['show_in_graphql'] ) || (boolean) $field_group['show_in_graphql']; /** * Determine if the field group should be exposed @@ -1244,17 +1276,17 @@ protected function add_field_group_fields( array $field_group, string $type_name foreach ( $acf_fields as $acf_field ) { if ( in_array( $acf_field['key'], $processed_keys, true ) ) { continue; - } else { - $processed_keys[] = $acf_field['key']; } + $processed_keys[] = $acf_field['key']; + /** * Setup data for register_graphql_field */ $explicit_name = ! empty( $acf_field['graphql_field_name'] ) ? $acf_field['graphql_field_name'] : null; $name = empty( $explicit_name ) && ! empty( $acf_field['name'] ) ? self::camel_case( $acf_field['name'] ) : $explicit_name; - $show_in_graphql = isset( $acf_field['show_in_graphql'] ) ? (bool) $acf_field['show_in_graphql'] : true; - $description = isset( $acf_field['instructions'] ) ? $acf_field['instructions'] : __( 'ACF Field added to the Schema by WPGraphQL ACF' ); + $show_in_graphql = ! isset( $acf_field['show_in_graphql'] ) || (bool) $acf_field['show_in_graphql']; + $description = $acf_field['instructions'] ?? __( 'ACF Field added to the Schema by WPGraphQL ACF' ); /** * If the field is missing a name or a type, @@ -1262,7 +1294,7 @@ protected function add_field_group_fields( array $field_group, string $type_name */ if ( empty( $name ) || - true != $show_in_graphql + true !== $show_in_graphql ) { /** @@ -1289,6 +1321,7 @@ protected function add_field_group_fields( array $field_group, string $type_name * Returns all available GraphQL Types * * @return array + * @throws Exception */ public static function get_all_graphql_types() { $graphql_types = array(); @@ -1319,9 +1352,10 @@ public static function get_all_graphql_types() { 'ContentTemplate' => [ 'label' => __( 'Page Template', 'wp-graphql-acf' ), 'plural_label' => __( 'All Templates Assignable to Content', 'wp-graphql-acf' ), - ] + ], ]; + foreach ( $interfaces as $interface_name => $config ) { $interface_query = graphql([ @@ -1349,6 +1383,18 @@ public static function get_all_graphql_types() { } + // Get all registered acf block types + $acf_block_types = acf_get_block_types(); + + // add ACF Block Types to the GraphQL Types array + if ( ! empty( $acf_block_types ) && is_array( $acf_block_types ) ) { + foreach ( $acf_block_types as $key => $acf_block_type ) { + $type_name = preg_replace( '/\//', '', lcfirst( ucwords( $key, '/' ) ) ); + $type_name = Utils::format_type_name( $type_name ); + $graphql_types[ $type_name ] = $type_name . ' (ACF Block)'; + } + } + /** * Add comment to GraphQL types */ @@ -1387,7 +1433,7 @@ public static function get_all_graphql_types() { if ( ! empty( $graphql_options_pages ) && is_array( $graphql_options_pages ) ) { /** - * Prepare type key prefix and label surfix + * Prepare type key prefix and label suffix */ $label = ' (' . __( 'ACF Options Page', 'wp-graphql-acf' ) . ')'; @@ -1435,18 +1481,16 @@ protected function add_acf_fields_to_graphql_types() { */ foreach ( $field_groups as $field_group ) { - $field_group_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : $field_group['title']; + $field_group_name = $field_group['graphql_field_name'] ?? $field_group['title']; $field_group_name = Utils::format_field_name( $field_group_name ); - $manually_set_graphql_types = isset( $field_group['map_graphql_types_from_location_rules'] ) ? (bool) $field_group['map_graphql_types_from_location_rules'] : false; + $manually_set_graphql_types = isset( $field_group['map_graphql_types_from_location_rules'] ) && (bool) $field_group['map_graphql_types_from_location_rules']; - if ( false === $manually_set_graphql_types ) { - if ( ! isset( $field_group['graphql_types'] ) || empty( $field_group['graphql_types'] ) ) { - $field_group['graphql_types'] = []; - $location_rules = $this->get_location_rules(); - if ( isset( $location_rules[ $field_group_name ] ) ) { - $field_group['graphql_types'] = $location_rules[ $field_group_name ]; - } + if ( ( false === $manually_set_graphql_types ) && empty( $field_group['graphql_types'] ) ) { + $field_group['graphql_types'] = []; + $location_rules = $this->get_location_rules(); + if ( isset( $location_rules[ $field_group_name ] ) ) { + $field_group['graphql_types'] = $location_rules[ $field_group_name ]; } } @@ -1468,7 +1512,7 @@ protected function add_acf_fields_to_graphql_types() { /** * Prepare default info */ - $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] ); + $field_name = $field_group['graphql_field_name'] ?? self::camel_case( $field_group['title'] ); $field_group['type'] = 'group'; $field_group['name'] = $field_name; $config = [ @@ -1476,7 +1520,7 @@ protected function add_acf_fields_to_graphql_types() { 'acf_field' => $field_group, 'acf_field_group' => null, 'resolve' => function ( $root ) use ( $field_group ) { - return isset( $root ) ? $root : null; + return $root ?? null; } ]; From 9e4ad58b6f784da58a5fa149715b0478b4f0503d Mon Sep 17 00:00:00 2001 From: Jason Bahl Date: Thu, 15 Sep 2022 09:50:55 -0600 Subject: [PATCH 2/2] - add more strict check on checking if the field group is active --- src/class-config.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/class-config.php b/src/class-config.php index 319bb94..e75d4d2 100644 --- a/src/class-config.php +++ b/src/class-config.php @@ -97,7 +97,7 @@ public function init( TypeRegistry $type_registry ) { // For flex fields/repeaters, the meta keys are structured a bit funky. // This checks to see if the $meta_key starts with the same string as one of the - // acf fields (a flex/repeater field) and then checks if it's preceeded by an underscore and a number. + // acf fields (a flex/repeater field) and then checks if it's preceded by an underscore and a number. if ( $field_name === substr( $meta_key, 0, strlen( $field_name ) ) ) { // match any string that starts with the field name, followed by an underscore, followed by a number, followed by another string // ex my_flex_field_0_text_field or some_repeater_field_12_25MostPopularDogToys @@ -315,8 +315,9 @@ protected function should_field_group_show_in_graphql( $field_group ) { * root groups, not nested groups with parent. */ if ( ! isset( $field_group['parent'] ) ) { + if ( - ( isset( $field_group['active'] ) && true != $field_group['active'] ) || + ( isset( $field_group['active'] ) && true !== $field_group['active'] ) || ( empty( $field_group['location'] ) || ! is_array( $field_group['location'] ) ) ) { $show = false;