📝

WordPressからmicroCMSに記事を移行した時の覚え書き

2020/12/25に公開
7

この記事はJamstack Advent Calendar 2020の21日目の記事です。だいぶ遅くなってしまってすみません。

WordPressで作られたサイトをmicroCMSに移行する機会があったので、その時に行った記事移行のやり方の覚え書きになります。

前提

  • 移行する記事データはWordPressデフォルトのエクスポート機能で生成されたXML
  • microCMSのPOST APIを使用
    • CSVインポート機能があるが記事が0の時しか使えないのと、インポート用に整形する手間を考えて使わない
  • サンプルで出てくるプログラムはPHPで書かれています

microCMS側の準備

microCMSのアカウント登録とサービス作成は終わっている前提で書いています。

X-WRITE-API-KEYを作成

サービスの設定画面のAPI-KEYの項目でX-WRITE-API-KEYの 新規作成 から認証キーを作成します。
この認証キーは後で使用します。

コンテンツのHTTPメソッドの設定でPOSTを許可する

HTTPメソッドの設定はコンテンツごとにできるので、サービスではなくコンテンツのAPI設定からPOSTを許可します。(デフォルトではGETだけ有効)

APIスキーマの設定

移行するデータのためにAPIスキーマを設定します。
今回は以下のようなスキーマを作成しました。

  • title(タイトル) : テキストフィールド
  • category(カテゴリ) : 複数コンテンツ参照(別のコンテンツから参照する)
  • contents(本文) : リッチエディタ
  • is_html(HTMLエディタを使う) : 真偽値
  • html_contents(HTMLエディタ) : テキストエリア
  • is_migration(移行記事) : 真偽値

互換性の問題に対する対応

WordPressのWysiWygエディタで書かれたデータはエクスポートされた時にHTMLになっているので、そのままmicroCMSのリッチエディタに登録するとうまくいきません。

移行した記事はほとんど編集することもないだろうということで、html_contents(HTMLエディタ) という移行記事用のフィールドを用意しておき、 用意したチェックボックス is_html(HTMLエディタを使う) のチェックの有無で contentshtml_contents のどちらを使うかフロント側で判定できるようにしました。

また、移行後にHTMLエディタを使いたいケースはあるかもしれないので、さらに is_migration(移行記事) というチェックボックスを用意して移行記事と新しい記事でなにか処理を変える必要があった場合に対応できるようにしました。(今のところは使っていない)

画像の準備

画像のリネーム

WordPressの画像はデフォルトだと /wp-content/uploads/{YYYY}/{MM}/{fileName} というフォルダ構造ですが、microCMSのメディアは階層構造がないので、そのままだと画像パスの置換が上手くいかずに移行できません。
そのため {YYYY}_{MM}_fileName のようなファイル名にリネームするプログラムを書きました。
(ローカルで実行する想定で、実行するところの wp-content/uploads/ に画像がある想定です)

<?php
// wp-content/uploads/ 以下の画像をすべて取得
$images = glob("wp-content/uploads/*/*/*.*");

foreach($images as $image) {
    // 画像パスを `/` で分解して逆順にする
    $pathArray = explode("/", $image);
    $paths = array_reverse($pathArray);

    // 画像を取得
    $data = file_get_contents($image);

    // ファイル名を `YYYY_MM_fileName` で `renamed_img` フォルダに保存
    file_put_contents("./renamed_img/{$paths[2]}_{$paths[1]}_{$paths[0]}", $data);
}

実行すると renamed_img というフォルダに保存されるようになっています。 renamed_img フォルダは予め作っておく必要があります。

実行するとこんな感じになります。

画像のアップロード

リネームした画像は手動でアップロードする必要がありますが、microCMSの管理画面のメディアからドラッグ&ドロップで複数アップロードすることが可能です。
実際に移行した時は2000件ほどの画像がありましたが、途中で止まることもなく10分ほど待っていたら無事に画像のアップロードをすることができました。

記事内容を移行するプログラム

microCMSのPOST APIは、APIスキーマに沿って送信したい内容をJSON形式で送ることでデータを記事として登録できます。詳細については公式のドキュメント を参照ください。

WordPressのXMLからJSONを作成してPOSTするプログラムを書いていきます。

設定部分

環境によって異なる部分なので適宜変更してください。

// 移行記事のxmlファイルを指定
$xml = "./foo.xml";

// microCMSの画像用CDNのURLとパス
$imgBasePath = 'https://images.microcms-assets.io/bar/';

//xmlを読み込み
$xmlData = simplexml_load_file($xml, 'SimpleXMLElement', LIBXML_NOCDATA);

$url = 'microCMSのAPIのエンドポイント';
$headers = array(
    'Content-Type: application/json',
    'X-WRITE-API-KEY: 取得したAPI-KEY'
);

記事データをループ

記事データごとに必要な処理を入れてPOSTしていきます。

foreach ($xmlData->channel->item as $entry) {
    // ここに処理を書いていく
}

以下、ループの中身の処理について書いていきます。

カテゴリの設定

カテゴリはXMLには以下のような形式で保存されています。

<item>
    <category domain="category" nicename="topics01"><![CDATA[お知らせ01]]></category>
    <category domain="category" nicename="topics02"><![CDATA[お知らせ02]]></category>
</item>

["参照先id1","参照先id2"] のような配列の形でPOSTできるように処理していきます。
この時、$cateArray[] に入るのは、microCMSで作っているカテゴリのIDに合わせておきます。
(nicenameの取得の仕方がわからなかったので力技になりました)

// カテゴリの設定
$cateArray = [];
$categories = json_decode(json_encode($entry->category), true);

foreach ($categories as $category) {
    if (is_array($category)) continue;

    switch($category) {
        case 'お知らせ01':
            $cateArray[] = 'topics01';
            break;
        case 'お知らせ02':
            $cateArray[] = 'topics02';
            break;
        case 'お知らせ03':
            $cateArray[] = 'topics03';
            break;
    }
}

HTML内の画像のパス置換

画像のURLが変わるのでmicroCMSでアップロードした画像のURLになるように置換します。

// コンテンツ取得
$contents = trim(strval($entry->children('content', true)->encoded));

// 画像を使っている部分を正規表現で抽出
preg_match_all('/src="https:\/\/foober\.com\/wp-content\/uploads\/(.*)(png|jpg|jpeg|gif)/i', $contents, $matches);

// 置き換え用の配列を作成
$pattern = [];
$replace = [];
foreach ($matches[0] as $key => $match) {
    $pattern[] = '/' . preg_replace('/\//', '\/', $match) . '/';
    $pathArray = explode('/', $match);
    $paths = array_reverse($pathArray);
    $replace[] = 'src="' . $imgBasePath . "{$paths[2]}_{$paths[1]}_{$paths[0]}";
}

// 置き換え実行
$html_content = preg_replace($pattern, $replace, $contents);

データのPOST

送信用のデータを作成してcurlでPOSTします。

// 送信用のデータ作成
$data = [
    'id' => '000' . strval($entry->children('wp', true)->post_id),
    'title' => trim(strval($entry->title)),
    'category' => array_unique($cateArray),
    'html_contents' => $html_content,
    'is_html' => true,
    'is_migration' => true
];

// curlでPOSTする
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$html = curl_exec($ch);

// curlでエラー
if(curl_errno($ch)){
    $CURLERR .= 'curl_errno:' . curl_errno($ch) . "\n";
    $CURLERR .= 'curl_error:' . curl_error($ch) . "\n";
    $CURLERR .= '▼curl_getinfo' . "\n";
    foreach(curl_getinfo($ch) as $key => $val){
        $CURLERR .= '■' . $key . ':' . $val . "\n";
    }
    echo nl2br($CURLERR);
}

curl_close($ch);

// 結果の表示
echo $data['id'] . ':' . $html . "\n";

IDは指定しなければ自動で振られますが、リダイレクトを行うためにWordPressの記事IDを含んだものを指定しています。コード全体はこちら

実行してみます。

$ php wp_to_microcms.php

0009137:{"message":"duplicate id."}
0009150:{"id":"0009150"}
0009158:{"id":"0009158"}
...

{"id":"XXXXXX"} のような結果が返却されていれば成功です。
一度テストで入れた記事がIDが重複していたのでエラーになっていますが、どの記事がどんな理由でエラーになっているのかわかりますね。

microCMSの管理画面を見てみるとちゃんと記事が登録されていました。

以上です。誰かの参考になれば嬉しいです!

あ、microCMSはプランによってWRITEリクエスト数が異なるのでお気をつけ下さい。
2021/9/1よりリクエスト数の制限がなくなったようです!
WRITE APIおよびコンテンツ数に関する制限改正予定のお知らせ

Discussion

t_tomot_tomo

非常に参考になりました。
ありがとうございます。

まった | maztak.ethまった | maztak.eth

参考になりました!一応記事の移行はできたものの、やはり日々色々と更新がされるなかでうまく行かない部分もありました。。

  • API キーのヘッダー名は X-MICROCMS-API-KEY になりました
  • 画像URLのパスが各画像共通ではなくなったので単純な置換はできなくなった
  • 代わりに Management API で画像URLの一覧は取得できるように

その他、注意点など

  • id は50文字までなので WP の slug を入れたい方は別途 slug フィールドを作ったほうがよさそう
  • POST時に createdAt, publishedAt の設定は出来ないので記事公開日が移行日になってしまう

HeadlessCMSが台頭してきてWPからの移行ニーズは多そうなんですけどね。どこも移行用プラグインとかは出されてなくてビジネス的な事情もあるんだろうなーと。

kandaikandai

ありがとうございます!更新情報や注意点などありがとうございます!
こちらでも確認して記事に追記していこうと思います!

Toshiki OkazakiToshiki Okazaki

大変参考になりました。WordPress -> microCMS の移行を検討しており、記事の移行手順で悩んでいたので、とても助かりました。ありがとうございます。

nicename ですが、SimpleXML であれば attributes() で取得できました!

    foreach ($entry->category as $term) {
        $attr = $term->attributes();
        if (!isset($attr['nicename']) || !isset($attr['domain'])) {
            continue;
        }
        $slug = (string) $attr['nicename'];
        $type = (string) $attr['domain'];
        switch ($type) {
            case 'category':
                $categorySlug = $slug;
                break;
            :
        }
    }
kandaikandai

参考になったようで良かったです!

nicename ですが、SimpleXML であれば attributes() で取得できました!

おぉ、そうなんですね!情報ありがとうございます!

シンハラシンハラ

こんにちは。

記事のご執筆ありがとうございます!
こちらの記事は現在も多くの方がご参考にされているようで、大変感謝しています!

先日のアップデートで、画像についても、APIでの登録が可能になりましたので、より便利にご登録いただけるようになっております👍
https://help.microcms.io/ja/knowledge/add-image-api

またコメントいただいている公開日時の登録については、publishedAtがAPIでの登録に対応いたしました👍
https://blog.microcms.io/update-api-for-published-date/

記事をご覧になった方のご参考となればと思い、コメントさせていただきました🙇‍♀️

kandaikandai

シンハラさん、アップデートの情報をありがとうございます!