の前)に以下を追記するだけで使用可能:
// 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 '
';
// 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 '
';
}
echo '
'; // インポートパネル終了
echo '
'; // flex終了
// 注意事項
echo '
';
echo '
⚠️ 注意事項';
echo '- インポートは既存の投稿データ(ACFフィールド・タクソノミー)を上書きします。事前にバックアップを推奨します。
';
echo '- アイキャッチ画像のIDは上書きしません(移行先に既存画像がある場合は保護されます)。
';
echo '- 画像ファイル本体は別途FTPで移行先の
/wp-content/uploads/ にコピーしてください。 ';
echo '- インポート完了後、
/wp-content/imports/ のJSONファイルをFTPで削除してください。 ';
echo '
';
echo '
'; // wrap終了
}
霊園・墓地・墓石などお墓のことなら石乃家(いしのや)
- 墓石を建てるなら石乃家
-
石乃家では、業界でも珍しく経験豊富な自社職人が在籍しています。
細かなご要望やオーダーメイドのご相談にも柔軟に対応可能です。
一生に一度あるかないかの大切なお墓づくりだからこそ、
後悔のないご提案と丁寧な施工をお約束します。
初めてのお客様
お墓は一生で一度の大きな買物、いざ購入する段階になって、お墓の建て方がわからない方も多いようです。
また、ご希望の霊園によっては決まりがあり、事前に手順や流れをご確認する事でご満足頂ける事と思います。
お墓のキホン
墓石の種類・デザインが多様化し、個性的なデザイン墓石から墓地タイプに合わせた墓石などが増えてきました。
当社では、数多くの選択肢からご提案させて頂きます。お墓の建立後には、墓石十年保証書を発行しております。
購入から納骨までの流れ
資料請求
お墓を選ぶ際のご相談やご希望の霊園の資料請求をします。一緒にご家族に最適な選択を探しましょう。
霊園見学
希望の条件にあった霊園を実際に足を運んで見学し、周辺の環境や移動時間などを確認しましょう
申し込み
墓石のデザインやお墓の区画などを決めます。 故人への思いを込めて、素敵なお墓を作り上げましょう。
建立・納骨
お墓をご確認後、問題なければお引き渡しとなります。建立後に納骨を済ませ、故人を偲ぶ法要を行います。