/** * Theme Functions for DTM Sale Information Site * Includes: Maintenance Mode, Sorting/Filtering Logic, Script Enqueueing, Search Keyword Handling * Based on plan: sort_filter_plan_20250422 * Final version incorporating user's latest code and search keyword handling. */ /* --- Maintenance Mode Code --- function maintenance_mode() { if ( ! current_user_can( 'edit_themes' ) && ! is_user_logged_in() ) { wp_die( 'ただいまサイトメンテナンス中です。ご迷惑をおかけしますが、しばらくお待ちください。' ); } } add_action( 'get_header', 'maintenance_mode' ); */ // --- Sorting and Filtering Logic --- function my_custom_sale_item_query_mods( $query ) { // Exit if in admin area or not the main query. if ( is_admin() || ! $query->is_main_query() ) { return; } // --- ▼▼▼ MODIFICATION START: Check for sale_item archive OR search ▼▼▼ --- // Only apply modifications to the 'sale_item' post type archive. // Also check if it's a search result page IF the post_type is specifically set to 'sale_item' OR 'keyword' is used $is_sale_item_archive = $query->is_post_type_archive( 'sale_item' ); // Check if 'keyword' is set, implying a search from our custom form $is_sale_item_search_via_keyword = isset( $_GET['keyword'] ) && !empty( trim( $_GET['keyword'] ) ) && isset( $_GET['post_type'] ) && $_GET['post_type'] === 'sale_item'; // Also consider if 's' is set and post_type is sale_item (e.g., direct URL access) $is_sale_item_search_via_s = $query->is_search() && $query->get('post_type') === 'sale_item'; // Apply mods if it's the archive or a relevant search if ( $is_sale_item_archive || $is_sale_item_search_via_keyword || $is_sale_item_search_via_s ) { // --- ★★★ NEW: Handle 'keyword' parameter from custom search form ★★★ --- if ( isset( $_GET['keyword'] ) && ! empty( trim( $_GET['keyword'] ) ) ) { // Set the main query's search parameter ('s') from 'keyword' $query->set( 's', sanitize_text_field( $_GET['keyword'] ) ); // Ensure post_type is set correctly when using keyword if ($query->get('post_type') !== 'sale_item') { $query->set('post_type', 'sale_item'); } // Mark that this is a search query internally if needed $query->is_search = true; // Explicitly set search context } // --- ★★★ End NEW ★★★ --- // --- ▲▲▲ MODIFICATION END ▲▲▲ --- // --- Base Filtering: Only show 'on sale' items by default (Apply only on archive, not search results) --- // --- ▼▼▼ MODIFICATION START: Condition to apply base filter ▼▼▼ --- // Apply only if it's the archive page AND not a search triggered by 'keyword' or 's' if ( $is_sale_item_archive && ! $query->is_search() ) { // --- ▲▲▲ MODIFICATION END ▲▲▲ --- $show_all = isset( $_GET['show_all'] ) && $_GET['show_all'] === '1'; $meta_query = $query->get( 'meta_query' ) ?: []; if (!is_array($meta_query)) { $meta_query = []; } if (!isset($meta_query['relation'])) { $meta_query['relation'] = 'AND';} if ( ! $show_all ) { // ★★★ Verify 'is_on_sale' field name and value ('1', true, etc.) ★★★ $meta_query['on_sale_clause'] = [ 'key' => 'is_on_sale', 'value' => '1', // ★★★ Value Check ★★★ 'compare' => '=', ]; } // Only set meta query here if it was modified if (!empty($meta_query['on_sale_clause']) || isset($meta_query['relation'])) { $query->set( 'meta_query', $meta_query ); } } // --- Filtering Conditions (Applied to both archive and search if params exist) --- $meta_query = $query->get( 'meta_query' ) ?: []; // Get potentially modified meta query if (!is_array($meta_query)) { $meta_query = []; } if (!isset($meta_query['relation'])) { $meta_query['relation'] = 'AND';} // Filter by Sale Period (End Date) // ★★★ Verify 'soonest_sale_end_date' field name and Ymd format ★★★ if ( ! empty( $_GET['filter_period'] ) ) { $period = sanitize_key( $_GET['filter_period'] ); $today_ymd = current_time( 'Ymd' ); if ( $period === 'today' ) { $meta_query['period_clause'] = [ 'key' => 'soonest_sale_end_date', // ★★★ Name Check ★★★ 'value' => $today_ymd, 'compare' => '=', 'type' => 'DATE', ]; } elseif ( $period === 'within_week' ) { $one_week_later_ymd = date( 'Ymd', strtotime( '+6 days', current_time( 'timestamp' ) ) ); $meta_query['period_clause'] = [ 'key' => 'soonest_sale_end_date', // ★★★ Name Check ★★★ 'value' => [ $today_ymd, $one_week_later_ymd ], 'compare' => 'BETWEEN', 'type' => 'DATE', ]; } } // Filter by Price Range (USD based) // ★★★ Verify 'current_lowest_sale_price_usd' field name and numeric value ★★★ $min_price = isset( $_GET['min_price'] ) && is_numeric( $_GET['min_price'] ) ? (float) $_GET['min_price'] : null; $max_price = isset( $_GET['max_price'] ) && is_numeric( $_GET['max_price'] ) ? (float) $_GET['max_price'] : null; if ( $min_price !== null || $max_price !== null ) { if ( $min_price !== null && $max_price !== null && $min_price > $max_price ) { $temp = $min_price; $min_price = $max_price; $max_price = $temp; } if ( $min_price !== null && $max_price !== null ) { $meta_query['price_clause'] = [ 'key' => 'current_lowest_sale_price_usd', // ★★★ Name Check ★★★ 'value' => [ $min_price, $max_price ], 'compare' => 'BETWEEN', 'type' => 'DECIMAL(10,2)', // Adjust precision if needed ]; } elseif ( $min_price !== null ) { $meta_query['price_clause'] = [ 'key' => 'current_lowest_sale_price_usd', // ★★★ Name Check ★★★ 'value' => $min_price, 'compare' => '>=', 'type' => 'DECIMAL(10,2)', ]; } elseif ( $max_price !== null ) { $meta_query['price_clause'] = [ 'key' => 'current_lowest_sale_price_usd', // ★★★ Name Check ★★★ 'value' => $max_price, 'compare' => '<=', 'type' => 'DECIMAL(10,2)', ]; } } // Apply the combined meta query (again, to ensure all clauses are included) // Make sure not to overwrite existing meta query if only sorting is applied if (count($meta_query) > 1 || (count($meta_query) == 1 && !isset($meta_query['relation']))) { // Check if clauses were added $query->set( 'meta_query', $meta_query ); } // --- Sorting Conditions (Applied to both archive and search if param exists) --- if ( ! empty( $_GET['sort'] ) ) { $sort_key = sanitize_key( $_GET['sort'] ); switch ( $sort_key ) { case 'price_asc': // ★★★ Verify 'current_lowest_sale_price_usd' field name ★★★ $query->set( 'meta_key', 'current_lowest_sale_price_usd' ); $query->set( 'orderby', 'meta_value_num' ); $query->set( 'order', 'ASC' ); break; case 'price_desc': // ★★★ Verify 'current_lowest_sale_price_usd' field name ★★★ $query->set( 'meta_key', 'current_lowest_sale_price_usd' ); $query->set( 'orderby', 'meta_value_num' ); $query->set( 'order', 'DESC' ); break; case 'discount_desc': // ★★★ Verify 'discount_percentage' field name ★★★ $query->set( 'meta_key', 'discount_percentage' ); $query->set( 'orderby', 'meta_value_num' ); $query->set( 'order', 'DESC' ); break; case 'end_date_asc': // ★★★ Verify 'soonest_sale_end_date' field name ★★★ $current_meta_query_sort = $query->get('meta_query'); if (!is_array($current_meta_query_sort)) $current_meta_query_sort = []; $current_meta_query_sort['end_date_exists_clause'] = [ 'key' => 'soonest_sale_end_date', // ★★★ Name Check ★★★ 'compare' => 'EXISTS' ]; if (!isset($current_meta_query_sort['relation'])) $current_meta_query_sort['relation'] = 'AND'; $query->set('meta_query', $current_meta_query_sort); $query->set( 'orderby', [ 'meta_value' => 'ASC', 'date' => 'DESC' ] ); $query->set( 'meta_key', 'soonest_sale_end_date' ); // ★★★ Name Check ★★★ break; case 'date_desc': $query->set( 'orderby', 'date' ); $query->set( 'order', 'DESC' ); break; default: // Keep default order (relevance for search, date for archive) unless specified // --- ▼▼▼ MODIFICATION START: Default orderby condition ▼▼▼ --- if ( $is_sale_item_archive && !$query->is_search() && empty($query->get('orderby')) ) { // Apply default only if archive, not search and not already set $query->set( 'orderby', 'date' ); $query->set( 'order', 'DESC' ); } // For search, WordPress default relevance is usually fine // --- ▲▲▲ MODIFICATION END ▲▲▲ --- } } else { // Default sort order if no 'sort' param (date for archive, relevance for search) // --- ▼▼▼ MODIFICATION START: Default orderby condition ▼▼▼ --- if ( $is_sale_item_archive && !$query->is_search() ) { // Apply default only if archive and not search $query->set( 'orderby', 'date' ); $query->set( 'order', 'DESC' ); } // --- ▲▲▲ MODIFICATION END ▲▲▲ --- } } // end if $is_sale_item_archive || $is_sale_item_search_via_keyword || $is_sale_item_search_via_s } add_action( 'pre_get_posts', 'my_custom_sale_item_query_mods' ); // --- Generate HTML for Active Filters --- function my_get_current_filters_html() { $filters_html = ''; $base_url = get_post_type_archive_link( 'sale_item' ); $current_args = $_GET; $sort_labels = [ 'price_asc' => '価格が安い順 (USD)', 'price_desc' => '価格が高い順 (USD)', 'discount_desc' => '割引率が大きい順', 'end_date_asc' => 'セール終了が近い順', 'date_desc' => '新着順', ]; $period_labels = [ 'today' => '今日終了', 'within_week' => '1週間以内終了', ]; // --- ▼▼▼ NEW: Handle 'keyword' for filter display ▼▼▼ --- $search_term = isset( $current_args['keyword'] ) ? sanitize_text_field($current_args['keyword']) : ''; if ( !empty($search_term) ) { $remove_args = $current_args; unset( $remove_args['keyword'], $remove_args['s'] ); // Remove both keyword and s $remove_url = add_query_arg( $remove_args, $base_url ); $filters_html .= ''; } // --- ▲▲▲ NEW ▲▲▲ --- // Display Sort Order if ( ! empty( $current_args['sort'] ) ) { $current_sort_key = sanitize_key( $current_args['sort'] ); if ( isset( $sort_labels[$current_sort_key] ) ) { $remove_args = $current_args; unset( $remove_args['sort'] ); $remove_url = add_query_arg( $remove_args, $base_url ); $filters_html .= '
  • 並び順: ' . esc_html( $sort_labels[$current_sort_key] ) . ' ×
  • '; } } // Display Period Filter if ( ! empty( $current_args['filter_period'] ) ) { $current_period_key = sanitize_key( $current_args['filter_period'] ); if ( isset( $period_labels[$current_period_key] ) ) { $remove_args = $current_args; unset( $remove_args['filter_period'] ); $remove_url = add_query_arg( $remove_args, $base_url ); $filters_html .= '
  • 期間: ' . esc_html( $period_labels[$current_period_key] ) . ' ×
  • '; } } // Display Price Filter $min_price = isset( $current_args['min_price'] ) && is_numeric( $current_args['min_price'] ) ? (float) $current_args['min_price'] : null; $max_price = isset( $current_args['max_price'] ) && is_numeric( $current_args['max_price'] ) ? (float) $current_args['max_price'] : null; if ( $min_price !== null || $max_price !== null ) { $price_label = '価格 (USD): '; $remove_args = $current_args; unset( $remove_args['min_price'], $remove_args['max_price'] ); if ( $min_price !== null && $max_price !== null && $min_price <= $max_price ) { $price_label .= '$' . esc_html( number_format( $min_price, 2 ) ) . ' ~ $' . esc_html( number_format( $max_price, 2 ) ); } elseif ( $min_price !== null ) { $price_label .= '$' . esc_html( number_format( $min_price, 2 ) ) . ' 以上'; } elseif ( $max_price !== null ) { $price_label .= '$' . esc_html( number_format( $max_price, 2 ) ) . ' 以下'; } $remove_url = add_query_arg( $remove_args, $base_url ); $filters_html .= '
  • ' . $price_label . ' ×
  • '; } // Display "Clear All Filters" Link // --- ▼▼▼ MODIFICATION START: Include search term in condition ▼▼▼ --- $has_filters = ! empty( $current_args['sort'] ) || ! empty( $current_args['filter_period'] ) || isset( $current_args['min_price'] ) || isset( $current_args['max_price'] ) || ! empty( $search_term ); // Check if keyword filter is active // --- ▲▲▲ MODIFICATION END ▲▲▲ --- if ( $has_filters ) { $remove_all_args = []; if (isset($current_args['show_all']) && $current_args['show_all'] === '1') { $remove_all_args['show_all'] = '1'; } // Don't preserve s or keyword when clearing all $remove_all_url = add_query_arg( $remove_all_args, $base_url ); $filters_html .= '
  • すべてのフィルターを解除
  • '; } return $filters_html; } // --- Enqueue Scripts and Styles --- function my_enqueue_theme_scripts() { // Enqueue scripts/styles only on the relevant pages // --- ▼▼▼ MODIFICATION START: Condition for loading scripts ▼▼▼ --- $load_scripts = false; if ( is_post_type_archive( 'sale_item' ) || is_tax( 'maker' ) || is_tax( 'product_category' ) ) { $load_scripts = true; } elseif ( is_search() ) { // Load scripts on search results page ONLY if the search is for 'sale_item' // Check either 'post_type' GET param OR if the query object is set to 'sale_item' global $wp_query; // Make sure $wp_query is available // Check if post_type is explicitly set to sale_item in GET or query vars if ( (isset($_GET['post_type']) && $_GET['post_type'] === 'sale_item') || ($wp_query->get('post_type') === 'sale_item') ) { // Further check if the search was initiated via 'keyword' from our form if (isset($_GET['keyword'])) { $load_scripts = true; } // Or if it was a standard search ('s' param) targeting sale_item elseif (isset($_GET['s'])) { $load_scripts = true; } } } // --- ▲▲▲ MODIFICATION END ▲▲▲ --- if ( $load_scripts ) { // Enqueue noUiSlider wp_enqueue_style( 'nouislider-style', 'https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.1/nouislider.min.css', [], '15.7.1' ); wp_enqueue_script( 'nouislider-script', 'https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.1/nouislider.min.js', [], '15.7.1', true ); // Enqueue Custom Script wp_enqueue_script( 'my-custom-script', get_stylesheet_directory_uri() . '/js/script.js', ['jquery', 'nouislider-script'], '1.3', true ); // Version bump // Localize Script Data global $wpdb; // ★★★ Verify 'current_lowest_sale_price_usd' field name ★★★ $meta_key_for_max_price = 'current_lowest_sale_price_usd'; // Use variable for easier changes $max_price_db = $wpdb->get_var( $wpdb->prepare( "SELECT MAX( CAST( meta_value AS DECIMAL(10,2) ) ) FROM $wpdb->postmeta WHERE meta_key = %s", $meta_key_for_max_price ) ); $slider_max_price = $max_price_db ? ceil( (float) $max_price_db ) : 5000; // Default 5000 if no data $current_min_price = isset( $_GET['min_price'] ) && is_numeric( $_GET['min_price'] ) ? (float) $_GET['min_price'] : 0; $current_max_price = isset( $_GET['max_price'] ) && is_numeric( $_GET['max_price'] ) ? (float) $_GET['max_price'] : $slider_max_price; // Validate current min/max against slider range if ($current_max_price > $slider_max_price) { $current_max_price = $slider_max_price; } if ($current_min_price < 0) { $current_min_price = 0; } if ($current_min_price > $current_max_price) { $current_min_price = $current_max_price; } wp_localize_script( 'my-custom-script', 'saleItemData', [ 'currentMinPrice' => $current_min_price, 'currentMaxPrice' => $current_max_price, 'sliderMaxPrice' => $slider_max_price, 'currentQueryParams' => $_GET, // Pass all GET params 'ajax_url' => admin_url( 'admin-ajax.php' ) ]); } } add_action( 'wp_enqueue_scripts', 'my_enqueue_theme_scripts' ); // --- Other Theme Setup / Includes (Keep as provided by user) --- $inc_dir = get_stylesheet_directory() . '/inc'; require_once( $inc_dir . '/custom-seo.php' ); require_once( $inc_dir . '/admin-custom-tools.php' ); require_once( $inc_dir . '/custom-shortcode.php' ); require_once( $inc_dir . '/custom-widgets.php' ); require_once( $inc_dir . '/archive-only-sale.php' ); /** * Chart.js とカスタムグラフスクリプトを single-sale_item.php で読み込む * ★ 別のデータベースに接続し、USDとJPY両方のセール価格履歴を取得 (データ連携最終修正) ★ */ function enqueue_sale_item_chart_scripts() { // single-sale_item.php が表示されている場合のみ実行 if ( is_singular( 'sale_item' ) ) { global $post; // --- 1. Chart.js ライブラリの読み込み --- wp_enqueue_script( 'chart-js', 'https://cdn.jsdelivr.net/npm/chart.js@4.4.2/dist/chart.umd.min.js', array(), '4.4.2', true ); // --- 2. 自作グラフ描画スクリプトの読み込み --- $chart_js_path = get_stylesheet_directory() . '/js/price-chart.js'; $chart_js_uri = get_stylesheet_directory_uri() . '/js/price-chart.js'; if ( file_exists( $chart_js_path ) ) { wp_enqueue_script( 'price-chart', $chart_js_uri, array('chart-js'), filemtime( $chart_js_path ), true ); } else { error_log('Error: price-chart.js not found at ' . $chart_js_path); // スクリプトがない場合は localize も実行しない return; } // --- 3. 価格履歴データを取得・整形してJavaScriptに渡す --- $chart_labels = array(); $chart_data_usd = array(); $chart_data_jpy = array(); $reference_price_usd = null; // Initialize reference price // --- 3a. 参照価格(USD)を取得 --- $reference_price_usd_raw = get_field('reference_price_usd', $post->ID); // ★ 取得した値が数値または数値文字列の場合のみ float にキャスト、それ以外は null ★ if (isset($reference_price_usd_raw) && is_numeric($reference_price_usd_raw)) { $reference_price_usd = (float)$reference_price_usd_raw; } else { // error_log("[Price Chart] Reference USD price not found or invalid for post ID: " . $post->ID . ". Value: " . print_r($reference_price_usd_raw, true)); } // --- 3b. データベースから日ごとの最安セール価格(USD & JPY)を取得 --- $product_id_field = get_field('product_id', $post->ID); if ( !empty( $product_id_field ) && is_numeric( $product_id_field ) ) { $target_product_id = (int)$product_id_field; // error_log("[Price Chart DEBUG] Target Product ID: " . $target_product_id); // --- 別データベースへの接続 --- $custom_db = null; $db_error = null; if ( defined('CUSTOM_DB_USER') && defined('CUSTOM_DB_PASSWORD') && defined('CUSTOM_DB_NAME') && defined('CUSTOM_DB_HOST') ) { if (class_exists('wpdb')) { $custom_db = new wpdb( CUSTOM_DB_USER, CUSTOM_DB_PASSWORD, CUSTOM_DB_NAME, CUSTOM_DB_HOST ); if ( ! empty( $custom_db->error ) ) { $db_error = "Custom DB Connection Error: " . print_r($custom_db->error, true); error_log("[Price Chart DEBUG] " . $db_error); $custom_db = null; } // else { error_log("[Price Chart DEBUG] Custom DB Connection successful."); } // 成功ログ } else { $db_error = "wpdb class not found."; error_log("[Price Chart DEBUG] " . $db_error); } } else { $db_error = "Custom DB Credentials not defined."; error_log("[Price Chart DEBUG] " . $db_error); } // --- 接続ここまで --- if ( $custom_db instanceof wpdb ) { $table_name = 'price_history'; // ★ テーブル名確認 $sql_query_string = " SELECT DATE(created_at) as history_date, MIN(price_usd) as min_price_usd, MIN(price_jpy) as min_price_jpy FROM `{$table_name}` WHERE product_id = %d AND (price_usd IS NOT NULL OR price_jpy IS NOT NULL) AND created_at >= DATE_SUB(CURDATE(), INTERVAL 1095 DAY) GROUP BY DATE(created_at) ORDER BY history_date ASC"; if (isset($sql_query_string) && isset($target_product_id)) { $sql = $custom_db->prepare($sql_query_string, $target_product_id); if ($sql) { // error_log("[Price Chart DEBUG] Executing SQL: " . $sql); // ★実行するSQLをログ出力 $results = $custom_db->get_results( $sql, ARRAY_A ); if ($custom_db->last_error) { // error_log("[Price Chart DEBUG] Custom DB Query Error: " . $custom_db->last_error); } else { // ★クエリ成功後の結果件数をログ出力 // error_log("[Price Chart DEBUG] Custom DB Query returned " . count($results) . " rows."); if ( !empty( $results ) && is_array($results) ) { $wp_timezone = new DateTimeZone(wp_timezone_string()); // error_log("[Price Chart DEBUG] Processing " . count($results) . " results..."); // ★ループ開始ログ foreach ( $results as $row_index => $row ) { // ★各行の内容をログ出力 // error_log("[Price Chart DEBUG] Row " . $row_index . ": " . print_r($row, true)); if (isset($row['history_date']) && ($row['min_price_usd'] !== null || $row['min_price_jpy'] !== null) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $row['history_date'])) { $date_obj = DateTime::createFromFormat('Y-m-d', $row['history_date'], $wp_timezone); if ($date_obj instanceof DateTime) { $formatted_date = $date_obj->format('Y/m/d'); $usd_val = isset($row['min_price_usd']) ? (float)$row['min_price_usd'] : null; $jpy_val = isset($row['min_price_jpy']) ? (float)$row['min_price_jpy'] : null; $chart_labels[] = $formatted_date; $chart_data_usd[] = $usd_val; $chart_data_jpy[] = $jpy_val; // ★処理したデータをログ出力 // error_log("[Price Chart DEBUG] Processed Row " . $row_index . ": Date=" . $formatted_date . ", USD=" . var_export($usd_val, true) . ", JPY=" . var_export($jpy_val, true)); } else { // error_log("[Price Chart DEBUG] Failed to parse date in Row " . $row_index . ": " . $row['history_date']); } } else { // error_log("[Price Chart DEBUG] Invalid data or missing keys in Row " . $row_index . ": " . print_r($row, true)); } } // error_log("[Price Chart DEBUG] Finished processing results."); // ★ループ終了ログ } else { // error_log("[Price Chart] No valid price history rows found after query for product_id: " . $target_product_id); } } } else { // error_log("[Price Chart DEBUG] wpdb::prepare failed."); } } else { // error_log("[Price Chart DEBUG] SQL string or target_product_id missing."); } } else { // error_log("[Price Chart] Custom DB connection failed or invalid."); } } else { // error_log("[Price Chart] product_id field missing or invalid."); } // --- 4. JavaScriptにデータを渡す --- // 常に localize を実行し、JS側で最終判断させる // error_log("[Price Chart DEBUG] Final data for JS - Labels: " . count($chart_labels) . ", USD: " . count($chart_data_usd) . ", JPY: " . count($chart_data_jpy) . ", Ref: " . var_export($reference_price_usd, true)); wp_localize_script( 'price-chart', 'priceChartData', array( 'labels' => $chart_labels, // ラベル配列 (空の場合あり) 'data_usd' => $chart_data_usd, // USD価格配列 (null含む, 空の場合あり) 'data_jpy' => $chart_data_jpy, // JPY価格配列 (null含む, 空の場合あり) 'reference_usd' => $reference_price_usd // 参照価格 (float or null) ) ); } // end if is_singular('sale_item') } add_action( 'wp_enqueue_scripts', 'enqueue_sale_item_chart_scripts' );
    Warning: Cannot modify header information - headers already sent by (output started at /home/noybrog/er-music.jp/public_html/music-app/wp-content/themes/the-thor-child/functions.php:1) in /home/noybrog/er-music.jp/public_html/music-app/wp-includes/pluggable.php on line 1450

    Warning: Cannot modify header information - headers already sent by (output started at /home/noybrog/er-music.jp/public_html/music-app/wp-content/themes/the-thor-child/functions.php:1) in /home/noybrog/er-music.jp/public_html/music-app/wp-includes/pluggable.php on line 1453