<?php
/**
* Plugin Name: Shop Item Link
* Description: 別のWooCommerceストアから商品情報を取得し、ショートコードで表示します。(キャッシュ無効化済み)
* Version: 1.0
* Author: Kazuyoshi Furuta
*/
// 直接ファイルにアクセスされた場合の保護
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// ----------------------------------------------------
// 1. ショートコードの登録
// ----------------------------------------------------
add_shortcode( 'shop', 'shop_item_link_product_shortcode' );
// ----------------------------------------------------
// 2. ショートコードのコールバック関数
// ----------------------------------------------------
function shop_item_link_product_shortcode( $atts ) {
// ショートコード属性 (id属性にカンマ区切りの文字列を想定)
$atts = shortcode_atts( array(
'id' => '', // 商品ID (カンマ区切り文字列)
), $atts, 'woo_product' );
$ids_string = trim( $atts['id'] );
// IDが空の場合はエラー
if ( empty( $ids_string ) ) {
return '<p style="color: red;">[woo_product] エラー: IDを指定してください。</p>';
}
// カンマ区切り文字列を配列に変換し、数値のみを抽出
$product_ids = array_filter( array_map( 'intval', explode( ',', $ids_string ) ) );
if ( empty( $product_ids ) ) {
return '<p style="color: red;">[woo_product] エラー: 有効な商品IDが指定されていません。</p>';
}
// ----------------------------------------------------
// 3. 認証情報とURLの取得 (wp-config.phpから)
// ----------------------------------------------------
$consumer_key = defined( 'WC_EXTERNAL_API_CONSUMER_KEY' ) ? WC_EXTERNAL_API_CONSUMER_KEY : '';
$consumer_secret = defined( 'WC_EXTERNAL_API_CONSUMER_SECRET' ) ? WC_EXTERNAL_API_CONSUMER_SECRET : '';
$site_url = defined( 'WC_EXTERNAL_API_HOSTNAME' ) ? WC_EXTERNAL_API_HOSTNAME : '';
// 認証情報・ホスト名の欠落チェック
if ( empty( $consumer_key ) || empty( $consumer_secret ) || empty( $site_url ) ) {
// ... 致命的エラー出力 (変更なし) ...
$missing = [];
if ( empty($consumer_key) ) $missing[] = 'Consumer Key';
if ( empty($consumer_secret) ) $missing[] = 'Consumer Secret';
if ( empty($site_url) ) $missing[] = 'Hostname';
$error_msg = '【致命的エラー】wp-config.phpで以下の定数が未定義です: ' . implode(', ', $missing);
error_log( $error_msg );
return '<div style="background-color: #ffcccc; padding: 10px; border: 1px solid red;">' . esc_html($error_m\
sg) . '</div>';
// ----------------------------------------------------
// 4. 各IDを処理し、HTML出力を結合
// ----------------------------------------------------
$final_output = '<h2>こちらで注文できます</h2>';
foreach ( $product_ids as $product_id ) {
// APIからデータを取得し、HTMLを生成するメイン処理
$final_output .= shop_item_link_fetch_and_render( $product_id, $site_url, $consumer_key, $consumer_secret )\
;
}
return $final_output;
}
/**
* 個別の商品情報をAPIから取得し、HTMLをレンダリングする関数
* この関数は、ショートコードのループから呼ばれます。
*/
function shop_item_link_fetch_and_render( $product_id, $site_url, $consumer_key, $consumer_secret ) {
// ホスト名末尾のスラッシュを削除し、エンドポイントを構築
$url = rtrim($site_url, '/') . "/wp-json/wc/v3/products/{$product_id}";
$authorization = base64_encode( "{$consumer_key}:{$consumer_secret}" );
$response = wp_remote_get( $url, array(
'headers' => array(
'Authorization' => 'Basic ' . $authorization,
),
'timeout' => 15,
) );
// ----------------------------------------------------
// APIレスポンスのチェック (デバッグ出力)
// ----------------------------------------------------
if ( is_wp_error( $response ) ) {
$error_message = $response->get_error_message();
error_log( 'API Request WP_Error: ' . $error_message . ' for ID: ' . $product_id );
return '<div style="background-color: #ffcc99; padding: 10px; border: 1px solid orange;">【API通信エラー】I\
D ' . $product_id . '、エラー: ' . esc_html($error_message) . '</div>';
}
$status_code = wp_remote_retrieve_response_code( $response );
$body = wp_remote_retrieve_body( $response );
if ( $status_code < 200 || $status_code >= 300 ) {
// ステータスコードエラー (401:認証失敗, 404:商品見つからずなど)
error_log( 'API Response Code Error: ' . $status_code . '. Product ID: ' . $product_id . '. Body: ' . $body\
);
$debug_output = '<div style="background-color: #ffffcc; padding: 10px; border: 1px solid #c00;">';
$debug_output .= '【APIステータスエラー】商品ID ' . $product_id . ' の取得失敗 (Status Code: ' . $status_co\
de . ')<br>';
$error_obj = json_decode($body);
if (isset($error_obj->message)) {
$debug_output .= '詳細メッセージ: ' . esc_html($error_obj->message);
} else {
$debug_output .= 'サーバー応答ボディの抜粋: ' . esc_html(substr($body, 0, 100)) . '...';
}
$debug_output .= '</div>';
return $debug_output;
}
$product_data = json_decode( $body );
// JSONデコードの失敗をチェック
if ( json_last_error() !== JSON_ERROR_NONE ) {
error_log( 'JSON Decode Error: ' . json_last_error_msg() . '. ID: ' . $product_id . '. Body: ' . $body );
return '<div style="background-color: #ffcccc; padding: 10px; border: 1px solid red;">【データエラー】ID ' \
. $product_id . ' の取得データの解析に失敗しました。</div>';
}
if ( ! is_object( $product_data ) ) {
return '<p style="color: red;">指定された商品ID ' . $product_id . ' の商品が見つかりません。</p>';
}
// ----------------------------------------------------
// HTMLレンダリング
// ----------------------------------------------------
$name = esc_html( $product_data->name );
$price = number_format( floatval( $product_data->price ) );
$permalink = esc_url( $product_data->permalink );
$image_url = ! empty( $product_data->images ) ? esc_url( $product_data->images[0]->src ) : '';
$stock_status = esc_html( $product_data->stock_status );
ob_start();
?>
<a href="<?php echo $permalink; ?>" target="_blank" style="text-decoration: none; color: #0073aa;">
<div class="custom-woo-product product-id-<?php echo $product_id; ?>" style="border: 1px solid #ddd; padding: 1\
5px; margin: 15px 0; background-color:aliceblue;">
<h3 style="font-size:1.5em;"><?php echo $name; ?></h3>
<?php if ( $image_url ): ?>
<div class="product-image" style="margin-bottom: 10px;">
<img src="<?php echo $image_url; ?>" alt="<?php echo $name; ?>" loading="lazy" style="max-width: 80\
%; max-height: 300px;height: auto;">
</div>
<?php endif; ?>
<div class="product-details">
<p class="product-price" style="font-weight: bold; font-size: 1.2em;">参考価格: ¥<?php echo $price; ?><\
span style="color:#bbb; font-size:0.8em;">(別途消費税、送料がかかります)</span></p>
<p class="product-stock <?php echo $stock_status; ?>" style="color: <?php echo ( $stock_status === 'ins\
tock' ? 'green' : '#999' ); ?>;">
在庫: <?php echo ( $stock_status === 'instock' ? 'あり' : 'なし' ); ?>
</p>
</div>
</div>
</a>
<?php
return ob_get_clean();
}