repository = $repository; $this->coordinates = $coordinates; $this->field_groups = $field_groups ?? 'acf_get_field_groups'; $this->fields_of = $fields_of ?? 'acf_get_fields'; } /** * Whether ACF is active. */ public static function is_available(): bool { return function_exists( 'get_field' ) && function_exists( 'acf_get_field_groups' ); } /** * Hooks the integration when ACF is present. */ public function register(): void { if ( ! self::is_available() ) { return; } add_filter( 'mapsemble_location_sources', static function ( array $sources ): array { $sources[ MapConfig::SOURCE_ACF ] = __( 'Advanced Custom Fields (a Google Map field)', 'mapsemble' ); return $sources; } ); add_action( 'mapsemble_map_form_location_fields', array( $this, 'render_field_select' ) ); // Priority 20: after ACF has written its own meta for the save. add_action( 'acf/save_post', array( $this, 'mirror_on_save' ), 20 ); } /** * Renders the ACF field picker in the map config form. * * @param MapConfig $config The config being edited. */ public function render_field_select( MapConfig $config ): void { $fields = $this->get_map_fields(); // The data attribute lets the edit screen script disable and hide // the picker while another source is chosen; without the script it // stays usable. ?>


Field key => label. */ private function get_map_fields(): array { $options = array(); foreach ( (array) call_user_func( $this->field_groups ) as $group ) { $group_fields = (array) call_user_func( $this->fields_of, $group ); foreach ( $group_fields as $field ) { if ( is_array( $field ) && 'google_map' === ( $field['type'] ?? '' ) ) { $options[ (string) $field['key'] ] = sprintf( '%s — %s', (string) ( $group['title'] ?? '' ), (string) ( $field['label'] ?? $field['name'] ?? $field['key'] ) ); } } } return $options; } /** * Mirrors the configured ACF field into the canonical meta on save. * * @param int|string $post_id ACF save target (posts give an int/numeric string). */ public function mirror_on_save( $post_id ): void { if ( ! is_numeric( $post_id ) ) { // Options pages, terms, users — not ours. return; } $post_id = (int) $post_id; $config = $this->coordinates->find_config_for_post( $post_id ); if ( null === $config || MapConfig::SOURCE_ACF !== $config->location_source || '' === $config->acf_field ) { return; } $this->coordinates->write( $post_id, $this->read_field_coords( $post_id, $config->acf_field ) ); } /** * Reads lat/lng from an ACF google_map field value. * * @param int $post_id Post ID. * @param string $field_key ACF field key. * @return array{lat: float, lng: float}|null */ public function read_field_coords( int $post_id, string $field_key ): ?array { $value = get_field( $field_key, $post_id, false ); if ( ! is_array( $value ) || ! isset( $value['lat'], $value['lng'] ) || ! is_numeric( $value['lat'] ) || ! is_numeric( $value['lng'] ) ) { return null; } return array( 'lat' => (float) $value['lat'], 'lng' => (float) $value['lng'], ); } /** * Resolves a post's coordinates from its config's source. * * Used by the reindex loop; safe to call whether or not ACF is * active (an ACF-sourced config simply resolves to null then). * * @param int $post_id Post ID. * @param MapConfig $config The post's map config. * @return array{lat: float, lng: float}|null */ public function resolve( int $post_id, MapConfig $config ): ?array { if ( MapConfig::SOURCE_ACF === $config->location_source ) { return self::is_available() && '' !== $config->acf_field ? $this->read_field_coords( $post_id, $config->acf_field ) : null; } if ( MapConfig::SOURCE_META_KEYS === $config->location_source ) { return '' !== $config->lat_meta_key && '' !== $config->lng_meta_key ? $this->coordinates->read_from_meta_keys( $post_id, $config->lat_meta_key, $config->lng_meta_key ) : null; } return $this->coordinates->get( $post_id ); } }