の前)に以下を追記するだけで使用可能: // require_once get_template_directory() . '/migration-tool.php'; // // 【機能】 // - 任意の投稿タイプ x タクソノミー x タームで絞り込んでエクスポート // - インポート時に既存投稿を上書き(タイトル不一致は警告) // - サイトURL・WordPressアドレスを自動または手動で置換 // - ACFフィールド・シリアライズデータ対応 // - WP6.5 / WP7 両対応 // ============================================================ // インポート時にスキップする画像関連メタキー // (移行先で画像IDが異なるため上書きしない) function migration_tool_skip_meta_keys() { return [ '_thumbnail_id', '_wp_attached_file', '_wp_attachment_metadata', ]; } // URL置換ヘルパー関数 // 文字列・配列・シリアライズ済みデータ内のURLを再帰的に置換する function migration_tool_replace_url( $value, $src_url, $dst_url ) { if ( empty( $src_url ) || $src_url === $dst_url ) return $value; if ( is_array( $value ) ) { return array_map( function( $v ) use ( $src_url, $dst_url ) { return migration_tool_replace_url( $v, $src_url, $dst_url ); }, $value ); } if ( ! is_string( $value ) ) return $value; // シリアライズ済みデータはunserialize → 置換 → reserialize $unserialized = @unserialize( $value ); if ( $unserialized !== false || $value === 'b:0;' ) { $replaced = migration_tool_replace_url( $unserialized, $src_url, $dst_url ); return serialize( $replaced ); } return str_replace( $src_url, $dst_url, $value ); } // ------------------------------------------------------- // 管理画面メニューに「移行ツール」を追加 // ------------------------------------------------------- add_action( 'admin_menu', function () { add_menu_page( '移行ツール', '移行ツール', 'manage_options', 'migration-tool', 'migration_tool_page', 'dashicons-migrate', 100 ); } ); // ------------------------------------------------------- // Ajax:投稿タイプに紐づくタクソノミー一覧を返す // ------------------------------------------------------- add_action( 'wp_ajax_migration_get_taxonomies', function () { if ( ! current_user_can( 'manage_options' ) ) wp_die( '権限がありません' ); check_ajax_referer( 'migration_tool_nonce', 'nonce' ); $post_type = isset( $_POST['post_type'] ) ? sanitize_text_field( $_POST['post_type'] ) : ''; if ( ! $post_type ) wp_send_json_success( [] ); $taxonomies = get_object_taxonomies( $post_type, 'objects' ); $result = []; foreach ( $taxonomies as $slug => $tax ) { $result[] = [ 'slug' => $slug, 'label' => $tax->label ]; } wp_send_json_success( $result ); } ); // ------------------------------------------------------- // Ajax:タクソノミーのターム一覧を返す // ------------------------------------------------------- add_action( 'wp_ajax_migration_get_terms', function () { if ( ! current_user_can( 'manage_options' ) ) wp_die( '権限がありません' ); check_ajax_referer( 'migration_tool_nonce', 'nonce' ); $taxonomy = isset( $_POST['taxonomy'] ) ? sanitize_text_field( $_POST['taxonomy'] ) : ''; if ( ! $taxonomy ) wp_send_json_success( [] ); $terms = get_terms( [ 'taxonomy' => $taxonomy, 'hide_empty' => false ] ); $result = []; if ( ! is_wp_error( $terms ) ) { foreach ( $terms as $term ) { $result[] = [ 'slug' => $term->slug, 'name' => $term->name, 'count' => $term->count ]; } } wp_send_json_success( $result ); } ); // ------------------------------------------------------- // Ajax:タームに紐づく投稿のID・タイトル一覧を返す // ------------------------------------------------------- add_action( 'wp_ajax_migration_get_posts', function () { if ( ! current_user_can( 'manage_options' ) ) wp_die( '権限がありません' ); check_ajax_referer( 'migration_tool_nonce', 'nonce' ); $post_type = isset( $_POST['post_type'] ) ? sanitize_text_field( $_POST['post_type'] ) : ''; $taxonomy = isset( $_POST['taxonomy'] ) ? sanitize_text_field( $_POST['taxonomy'] ) : ''; $term_slug = isset( $_POST['term_slug'] ) ? sanitize_text_field( $_POST['term_slug'] ) : ''; if ( ! $post_type || ! $taxonomy || ! $term_slug ) wp_send_json_success( [] ); $posts = get_posts( [ 'post_type' => $post_type, 'posts_per_page' => -1, 'post_status' => 'any', 'tax_query' => [ [ 'taxonomy' => $taxonomy, 'field' => 'slug', 'terms' => $term_slug, ] ], ] ); $result = array_map( function ( $p ) { return [ 'id' => $p->ID, 'title' => $p->post_title ]; }, $posts ); wp_send_json_success( $result ); } ); // ------------------------------------------------------- // エクスポート処理(admin_post フック) // フォームからPOSTされたときにJSONを生成してダウンロード // ------------------------------------------------------- add_action( 'admin_post_migration_export', function () { if ( ! current_user_can( 'manage_options' ) ) wp_die( '権限がありません' ); check_admin_referer( 'migration_tool_nonce' ); $post_type = isset( $_POST['post_type'] ) ? sanitize_text_field( $_POST['post_type'] ) : ''; $taxonomy = isset( $_POST['taxonomy'] ) ? sanitize_text_field( $_POST['taxonomy'] ) : ''; $term_slug = isset( $_POST['term_slug'] ) ? sanitize_text_field( $_POST['term_slug'] ) : ''; $id_input = isset( $_POST['export_ids'] ) ? sanitize_text_field( $_POST['export_ids'] ) : ''; $ids = []; // IDが直接指定された場合はIDを優先 if ( ! empty( $id_input ) ) { $ids = array_filter( array_map( 'intval', explode( ',', $id_input ) ) ); } // タクソノミー x タームで絞り込む if ( empty( $ids ) && $post_type && $taxonomy && $term_slug ) { $ids = get_posts( [ 'post_type' => $post_type, 'posts_per_page' => -1, 'post_status' => 'any', 'fields' => 'ids', 'tax_query' => [ [ 'taxonomy' => $taxonomy, 'field' => 'slug', 'terms' => $term_slug, ] ], ] ); } if ( empty( $ids ) ) { wp_die( 'エクスポート対象が見つかりません。条件を確認してください。' ); } $data = []; foreach ( $ids as $post_id ) { $post = get_post( $post_id ); if ( ! $post ) continue; // タクソノミーのスラッグをURLデコードして保存 $taxonomies = []; foreach ( get_object_taxonomies( $post->post_type ) as $tax ) { $terms = get_the_terms( $post_id, $tax ); if ( $terms && ! is_wp_error( $terms ) ) { $taxonomies[ $tax ] = array_map( 'urldecode', wp_list_pluck( $terms, 'slug' ) ); } } // カスタムフィールド(ACF含む)を取得・重複排除 $meta = get_post_meta( $post_id ); $meta_out = []; foreach ( $meta as $key => $values ) { $unique = array_values( array_unique( $values ) ); $meta_out[$key] = ( count( $unique ) === 1 ) ? $unique[0] : $unique; } $data[] = [ 'ID' => $post->ID, 'post_title' => $post->post_title, 'post_content' => $post->post_content, 'post_excerpt' => $post->post_excerpt, 'post_status' => $post->post_status, 'post_name' => $post->post_name, 'post_type' => $post->post_type, 'post_date' => $post->post_date, 'post_modified' => $post->post_modified, 'permalink' => get_permalink( $post_id ), 'taxonomies' => $taxonomies, 'meta' => $meta_out, ]; } // site_info にエクスポート元のURLを記録する $output = [ 'site_info' => [ 'site_url' => rtrim( site_url(), '/' ), 'home_url' => rtrim( home_url(), '/' ), ], 'posts' => $data, ]; $filename = 'export-' . $post_type . '-' . date( 'Ymd-His' ) . '.json'; header( 'Content-Type: application/json; charset=UTF-8' ); header( 'Content-Disposition: attachment; filename=' . $filename ); echo json_encode( $output, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT ); exit; } ); // ------------------------------------------------------- // インポート処理 // /wp-content/imports/ のJSONを読み込んで既存投稿に上書き // ------------------------------------------------------- add_action( 'admin_init', function () { if ( ! isset( $_POST['migration_import_action'] ) ) return; if ( ! current_user_can( 'manage_options' ) ) wp_die( '権限がありません' ); check_admin_referer( 'migration_tool_nonce' ); $filename = isset( $_POST['import_file'] ) ? sanitize_file_name( $_POST['import_file'] ) : ''; if ( ! $filename ) { set_transient( 'migration_tool_msg', [ 'error', 'ファイルが指定されていません。' ], 30 ); wp_redirect( admin_url( 'admin.php?page=migration-tool' ) ); exit; } $filepath = WP_CONTENT_DIR . '/imports/' . $filename; if ( ! file_exists( $filepath ) ) { set_transient( 'migration_tool_msg', [ 'error', 'ファイルが見つかりません: ' . $filename ], 30 ); wp_redirect( admin_url( 'admin.php?page=migration-tool' ) ); exit; } $json = file_get_contents( $filepath ); $decoded = json_decode( $json, true ); if ( ! is_array( $decoded ) ) { set_transient( 'migration_tool_msg', [ 'error', 'JSONの読み込みに失敗しました。' ], 30 ); wp_redirect( admin_url( 'admin.php?page=migration-tool' ) ); exit; } // 新形式(site_info + posts)と旧形式(配列のみ)の両方に対応 $site_info = $decoded['site_info'] ?? []; $data = isset( $decoded['posts'] ) ? $decoded['posts'] : $decoded; // URL置換の設定 $do_replace_url = ! empty( $_POST['replace_url'] ); $manual_src = isset( $_POST['manual_src_url'] ) ? rtrim( sanitize_text_field( $_POST['manual_src_url'] ), '/' ) : ''; $manual_dst = isset( $_POST['manual_dst_url'] ) ? rtrim( sanitize_text_field( $_POST['manual_dst_url'] ), '/' ) : ''; if ( $manual_src && $manual_dst ) { // 手動置換モード $replace_pairs = [ [ $manual_src, $manual_dst ] ]; } else { // 自動置換モード:JSONのsite_infoと移行先URLを比較 $replace_pairs = []; $dst_site_url = rtrim( site_url(), '/' ); $dst_home_url = rtrim( home_url(), '/' ); $src_site_url = rtrim( $site_info['site_url'] ?? '', '/' ); $src_home_url = rtrim( $site_info['home_url'] ?? '', '/' ); if ( $src_site_url && $src_site_url !== $dst_site_url ) { $replace_pairs[] = [ $src_site_url, $dst_site_url ]; } if ( $src_home_url && $src_home_url !== $dst_home_url && $src_home_url !== $src_site_url ) { $replace_pairs[] = [ $src_home_url, $dst_home_url ]; } } $skip_keys = migration_tool_skip_meta_keys(); $force_overwrite = ! empty( $_POST['force_overwrite'] ); $success = 0; $errors = []; $news = []; foreach ( $data as $item ) { $post_id = isset( $item['ID'] ) ? intval( $item['ID'] ) : 0; $existing = $post_id ? get_post( $post_id ) : null; // 本文・抜粋のURL置換 $post_content = $item['post_content'] ?? ''; $post_excerpt = $item['post_excerpt'] ?? ''; if ( $do_replace_url && ! empty( $replace_pairs ) ) { foreach ( $replace_pairs as $pair ) { $post_content = str_replace( $pair[0], $pair[1], $post_content ); $post_excerpt = str_replace( $pair[0], $pair[1], $post_excerpt ); } } $post_data = [ 'post_title' => $item['post_title'] ?? '', 'post_content' => $post_content, 'post_excerpt' => $post_excerpt, 'post_status' => $item['post_status'] ?? 'publish', 'post_name' => $item['post_name'] ?? '', 'post_type' => $item['post_type'] ?? 'post', 'post_date' => $item['post_date'] ?? '', ]; if ( $existing ) { // タイトル不一致かつ強制上書きOFFの場合はスキップ if ( $existing->post_title !== ( $item['post_title'] ?? '' ) && ! $force_overwrite ) { $errors[] = '⚠️ ID' . $post_id . ' のタイトルが不一致のためスキップしました。' . '(移行元:' . ( $item['post_title'] ?? '' ) . ' /' . ' 移行先:' . $existing->post_title . ')'; continue; } $post_data['ID'] = $post_id; $result = wp_update_post( $post_data, true ); } else { $result = wp_insert_post( $post_data, true ); $post_id = is_wp_error( $result ) ? 0 : $result; if ( ! is_wp_error( $result ) ) { $news[] = '🆕 ' . ( $item['post_title'] ?? '' ) . 'を新規作成しました。(新ID: ' . $post_id . ')'; } } if ( is_wp_error( $result ) ) { $errors[] = '❌ ' . ( $item['post_title'] ?? '不明' ) . ': ' . $result->get_error_message(); continue; } // カスタムフィールド(ACF含む)を上書き if ( ! empty( $item['meta'] ) && is_array( $item['meta'] ) ) { foreach ( $item['meta'] as $key => $value ) { if ( in_array( $key, $skip_keys, true ) ) continue; if ( $value === '' || $value === null || $value === [] ) continue; // URL置換 if ( $do_replace_url && ! empty( $replace_pairs ) ) { foreach ( $replace_pairs as $pair ) { $value = migration_tool_replace_url( $value, $pair[0], $pair[1] ); } } delete_post_meta( $post_id, $key ); if ( is_array( $value ) ) { foreach ( $value as $v ) { $store = @unserialize( $v ); add_post_meta( $post_id, $key, ( $store !== false || $v === 'b:0;' ) ? $store : $v ); } } else { $store = @unserialize( $value ); update_post_meta( $post_id, $key, ( $store !== false || $value === 'b:0;' ) ? $store : $value ); } } } // タクソノミーを上書き if ( ! empty( $item['taxonomies'] ) && is_array( $item['taxonomies'] ) ) { foreach ( $item['taxonomies'] as $taxonomy => $slugs ) { $term_ids = []; foreach ( (array) $slugs as $slug ) { $term = get_term_by( 'slug', $slug, $taxonomy ) ?: get_term_by( 'slug', urlencode( $slug ), $taxonomy ); if ( $term && ! is_wp_error( $term ) ) { $term_ids[] = $term->term_id; } } if ( ! empty( $term_ids ) ) { wp_set_object_terms( $post_id, $term_ids, $taxonomy ); } } } $success++; } $parts = []; if ( $success ) $parts[] = '✅ ' . $success . '件のインポートが完了しました。'; if ( ! empty( $news ) ) $parts[] = implode( ' / ', $news ); if ( ! empty( $errors ) ) $parts[] = implode( ' / ', $errors ); $msg = implode( ' ', $parts ) ?: 'インポート対象がありませんでした。'; $type = empty( $errors ) ? 'success' : 'warning'; set_transient( 'migration_tool_msg', [ $type, $msg ], 30 ); wp_redirect( admin_url( 'admin.php?page=migration-tool' ) ); exit; } ); // ------------------------------------------------------- // 移行ツールページの表示 // ------------------------------------------------------- function migration_tool_page() { $msg = get_transient( 'migration_tool_msg' ); if ( $msg ) { delete_transient( 'migration_tool_msg' ); $colors = [ 'success' => '#46b450', 'warning' => '#ffb900', 'error' => '#dc3232' ]; $color = $colors[ $msg[0] ] ?? '#46b450'; echo '
' . esc_html( $msg[1] ) . '
'; } $import_dir = WP_CONTENT_DIR . '/imports/'; if ( ! file_exists( $import_dir ) ) wp_mkdir_p( $import_dir ); $json_files = glob( $import_dir . '*.json' ) ?: []; $post_types = get_post_types( [ 'public' => true ], 'objects' ); $nonce = wp_create_nonce( 'migration_tool_nonce' ); echo '
'; echo '

🔄 移行ツール

'; echo '

任意の投稿タイプをJSON形式でエクスポート・インポートします。

'; echo '
'; // ========================================== // エクスポートパネル // ========================================== echo '
'; echo '

📤 エクスポート

'; echo '

投稿タイプ → タクソノミー → タームの順に選択してエクスポートします。
'; echo 'ダウンロードしたJSONを移行先の /wp-content/imports/ にFTPでアップロードしてください。

'; echo '
'; echo ''; wp_nonce_field( 'migration_tool_nonce' ); echo ''; // ① 投稿タイプ echo ''; // ② タクソノミー echo ''; // ③ ターム echo ''; // ④ 対象投稿一覧 echo ''; // IDを直接指定 echo ''; echo '
① 投稿タイプ'; echo '
または投稿IDを直接指定'; echo ''; echo '

カンマ区切りで複数指定可。IDを指定した場合はIDが優先されます。

'; echo '
'; echo '

'; echo '
'; // JavaScript(動的ドロップダウン) echo ''; echo '
'; // エクスポートパネル終了 // ========================================== // インポートパネル // ========================================== echo '
'; echo '

📥 インポート

'; echo '

FTPで /wp-content/imports/ にアップロードしたJSONを読み込み、既存の投稿に上書きインポートします。
'; echo 'アイキャッチ画像・空の値は既存データを保持します。

'; if ( empty( $json_files ) ) { echo '
'; echo '

📂 /wp-content/imports/ にJSONファイルが見つかりません。

'; echo '

FTPでアップロード後、ページを再読み込みしてください。

'; echo '
'; } else { echo '
'; wp_nonce_field( 'migration_tool_nonce' ); echo ''; echo ''; // ファイル選択 echo ''; // URL置換 echo ''; // タイトル不一致の場合 echo ''; echo '
インポートするファイル'; echo ''; echo '

' . count( $json_files ) . '件のファイルが見つかりました。

'; echo '
URL置換'; echo ''; echo '
'; echo '

🔄 自動置換(推奨)

'; echo '

JSONに記録された移行元URLを移行先URL(' . esc_html( home_url() ) . ')に自動置換します。

'; echo '

✏️ 手動置換(任意・入力時は手動が優先)

'; echo ''; echo ''; echo ''; echo ''; echo ''; echo '
置換前URL:
置換後URL:
'; echo ''; echo '
タイトル不一致の場合'; echo ''; echo '

⚠️ チェックを入れると、移行先に異なる投稿が登録されていても強制上書きされます。通常はチェック不要です。

'; echo '
'; echo '

'; echo '
'; } echo '
'; // インポートパネル終了 echo '
'; // flex終了 // 注意事項 echo '
'; echo '⚠️ 注意事項
'; echo '
'; // wrap終了 } 宗教不問 | 石乃家(いしのや) |64ページ目

宗教不問の霊園・墓地一覧

地域や条件から探す

よくあるご質問