acf_field_repeater::format_value_for_jsonldpublicACF 6.8.0

Formats the field value for JSON-LD output.

Method of the class: acf_field_repeater{}

No Hooks.

Returns

Mixed.

Usage

$acf_field_repeater = new acf_field_repeater();
$acf_field_repeater->format_value_for_jsonld( $value, $post_id, $field );
$value(mixed) (required)
The value of the field.
$post_id(int|string) (required)
The ID of the post.
$field(array) (required)
The field array.

Changelog

Since 6.8.0 Introduced.

acf_field_repeater::format_value_for_jsonld() code ACF 6.8.8

public function format_value_for_jsonld( $value, $post_id, $field ) {
	if ( empty( $value ) || ! is_array( $value ) || empty( $field['sub_fields'] ) ) {
		return null;
	}

	// Get output format with fallback.
	$output_format = $field['schema_output_format'] ?? '';
	if ( empty( $output_format ) ) {
		$property      = $field['schema_property'] ?? '';
		$output_format = \ACF\AI\GEO\Schema::get_default_output_format( $this->name, $property );
	}

	// Get all Schema.org types for type detection.
	$all_types   = array_keys( \ACF\AI\GEO\SchemaData::get_type_hierarchy() );
	$all_types[] = 'Thing';

	$items = array();

	// Process each row.
	foreach ( $value as $row ) {
		if ( ! is_array( $row ) ) {
			continue;
		}

		$row_data = array();
		$row_type = null;

		// Add @type to each row item if we have a specific output format.
		// For ItemList, items don't get the @type directly (they get wrapped in ListItem).
		if ( ! empty( $output_format ) && 'ItemList' !== $output_format ) {
			$row_type = $output_format;
		}

		// Process each sub field in the row.
		foreach ( $field['sub_fields'] as $sub_field ) {
			// Get the sub field value - try both key and name.
			$sub_value = $row[ $sub_field['key'] ] ?? $row[ $sub_field['_name'] ] ?? $row[ $sub_field['name'] ] ?? null;

			if ( null === $sub_value ) {
				continue;
			}

			// Check if sub field has a schema property mapping.
			$schema_property = $sub_field['schema_property'] ?? '';

			if ( empty( $schema_property ) ) {
				// No schema property - skip this field for JSON-LD output.
				continue;
			}

			// Check if schema_property is actually a Schema.org type (e.g., HowToStep).
			// If so, use it as the row's @type instead of as a property.
			if ( in_array( $schema_property, $all_types, true ) ) {
				$row_type = $schema_property;
				continue;
			}

			// Parse qualified property (e.g., "Thing.name" -> "name") to strip type prefix.
			$property_name = \ACF\AI\GEO\Schema::get_property_name( $schema_property );

			// Format the sub field value for JSON-LD.
			$formatted_value = \ACF\AI\GEO\GEO::format_field_value_for_jsonld( $sub_value, $sub_field );

			if ( null !== $formatted_value ) {
				$row_data[ $property_name ] = $formatted_value;
			}
		}

		// Add @type if we determined one.
		if ( $row_type ) {
			$row_data = array_merge( array( '@type' => $row_type ), $row_data );
		}

		// If row has only one property and no @type, extract just the value for cleaner output.
		if ( count( $row_data ) === 1 && ! isset( $row_data['@type'] ) ) {
			$items[] = reset( $row_data );
		} elseif ( ! empty( $row_data ) ) {
			// Only add row if it has content.
			$items[] = $row_data;
		}
	}

	if ( empty( $items ) ) {
		return null;
	}

	// For ItemList, wrap items in the proper structure.
	if ( 'ItemList' === $output_format ) {
		$list_items = array();
		foreach ( $items as $position => $item ) {
			$list_items[] = array(
				'@type'    => 'ListItem',
				'position' => $position + 1,
				'item'     => $item,
			);
		}
		return array(
			'@type'           => 'ItemList',
			'itemListElement' => $list_items,
		);
	}

	return $items;
}