*/ public function map( ProductRecord $record, ?ImageHints $imageHints = null, ?UsageHints $usageHints = null ): array { return array( 'id' => $record->id(), 'provider' => $record->provider(), 'provider_item_id' => $record->providerItemId(), 'title' => $record->title(), 'brand' => $record->brand(), 'description' => $record->description(), 'images' => array( 'primary' => $imageHints?->primaryUrl(), 'gallery' => $imageHints?->galleryUrls() ?? array(), ), 'price' => self::buildPrice( $record ), 'availability_status' => $record->availabilityStatus() ?? 'unknown', 'links' => array( 'detail_url' => $record->detailUrl(), 'affiliate_url' => $record->affiliateUrl(), ), 'sync' => array( 'last_synced_at' => self::formatRfc3339( $record->lastSyncedAt() ), 'last_sync_attempted_at' => self::formatRfc3339( $record->lastSyncAttemptedAt() ), 'sync_state' => $record->syncState(), 'lifecycle_state' => $record->lifecycleState(), 'stale_after' => null, 'last_sync_error_code' => $record->lastSyncErrorCode(), 'last_sync_error_message' => $record->lastSyncErrorMessage(), ), 'usage' => array( 'embed_count' => $usageHints?->embedCount() ?? 0, 'post_ids' => $usageHints?->postIds() ?? array(), ), ); } /** * Build the §6.2 `price` sub-object from a {@see ProductRecord}, * or null when the record carries no price amount. * * The `observed_at` field is sourced from * {@see ProductRecord::lastSyncedAt()} as a Phase-6 proxy; see * the file-level docblock for the rationale. * * @param ProductRecord $record Source record. * * @return array|null */ private static function buildPrice( ProductRecord $record ): ?array { $amount = $record->priceAmount(); if ( $amount === null ) { return null; } return array( 'amount' => $amount, 'currency' => $record->priceCurrency() ?? 'UNKNOWN', 'label' => $record->priceLabel(), 'observed_at' => self::formatRfc3339( $record->lastSyncedAt() ), ); } /** * Format a {@see DateTimeImmutable} as an RFC 3339 / ISO 8601 * UTC string per §2.4 ("原則: UTC で返す"), or null when * `$dt` is null. * * @param DateTimeImmutable|null $dt Timestamp to format, or null. */ private static function formatRfc3339( ?DateTimeImmutable $dt ): ?string { if ( $dt === null ) { return null; } return gmdate( 'Y-m-d\TH:i:s\Z', $dt->getTimestamp() ); } }