自前CMSのデータをWordPressに移行する

仕事でやる必要があって調べたのでメモ。

移行先のWordPressが、カスタムポストタイプや[ACF](http://www.advancedcustomfields.com/)を使ってたんで、ちょっと面倒くさかった。


# 大まかな流れ

自前CMSのデータから、WordPressインポートツールのWordPressのXMLを作成して、データ移行することにした。

ACFを使ってるWordPressサイトをXMLに書き出して中を見てみるとわかるけど、ACFはフィールドごとに`field_5671e74fc8c5e`みたいな識別子が振られてるみたいで、WordPressのXMLを作る際にこれが必要。
なので、段取りは以下のとおり。

1. 移行先のWordPressで、カスタムポストタイプやカスタムフィールドの設定をする。
2. 移行先のWordPressで、カスタムフィールドの値もちゃんと入力した、サンプルデータを登録する。
3. 移行先のWordPressで、XMLエキスポートする。その中身を見て、カスタムポストタイプやカスタムフィールドの識別子を確認する。
4. 移行元の自前CMSのデータを、WordPressインポートツール用のXMLに変換する。画像は、URLでアクセスできるようにしとく。
5. 移行先のWordPressで、作ったXMLをインポートする。


# WordPressインポートツール用のXML


## 大まかな構造

XMLの大まかな構造は以下のとおり。

![WordPressインポートツール用のXMLの構造](https://qiita-image-store.s3.amazonaws.com/0/8895/ceb1054c-7e2a-d0fd-133c-4f956ee281a7.png)

`author`、`category`、`term`は、そのまんまWordPressのユーザー、カテゴリー、タームに対応してる。

`item`は、記事、ページだけじゃなくて、画像、ACFのカスタムフィールドとかも`item`の中に入ってる。

各記事に登録したカスタムフィールドの値は、`postmeta`に入ってる。

`author`、`category`、`term`、`item`には、それぞれIDがふられてて、親子関係とか、itemに定義された画像の指定とかに使われる。

ちなみに、 `author`は、無くてもインポートできるっぽい。

## 画像

画像は、`item`として登録される。

画像のURLは、`attachment_url`という名前の子要素として定義する。

記事を表す`item`を親要素として登録しておけば、その記事に関連付けた画像として、ちゃんとインポートされる。

画像のカスタムフィールドには、itemに定義された画像のIDが入る。


## カスタムフィールドのデータ

カスタムフィールドのデータは、`postmeta`に入ってるんだけど、例えば「year」って名前のテキストフィールドがあるときは、こんな感じ。

```xml
<wp:postmeta>
    <wp:meta_key><![CDATA[year]]></wp:meta_key>
    <wp:meta_value><![CDATA[2015]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
    <wp:meta_key><![CDATA[_year]]></wp:meta_key>
    <wp:meta_value><![CDATA[field_5670f2bfd2c5a]]></wp:meta_value>
</wp:postmeta>
```

インポート用のXMLを作るためには、`year`と`field_5670f2bfd2c5a`を知っとかないといけない。


## 繰り返しフィールド

例えば、`images`って繰り返しフィールドがあって、その中に`image`って画像と、`caption`ってテキストフィールドが複数ある場合、下のような`postmeta`になる。
3行目の`2`は、定義された要素数が入ってるっぽい。

```xml
<wp:postmeta>
    <wp:meta_key><![CDATA[images]]></wp:meta_key>
    <wp:meta_value><![CDATA[2]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
    <wp:meta_key><![CDATA[_images]]></wp:meta_key>
    <wp:meta_value><![CDATA[field_5670f0d3391f8]]></wp:meta_value>
</wp:postmeta>

<!-- 一つ目の画像 -->
<wp:postmeta>
    <wp:meta_key><![CDATA[images_0_image]]></wp:meta_key>
    <wp:meta_value><![CDATA[10]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
    <wp:meta_key><![CDATA[_images_0_image]]></wp:meta_key>
    <wp:meta_value><![CDATA[field_5670f2bad2c5a]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
    <wp:meta_key><![CDATA[images_0_caption]]></wp:meta_key>
    <wp:meta_value><![CDATA[Image 1]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
    <wp:meta_key><![CDATA[_images_0_caption]]></wp:meta_key>
    <wp:meta_value><![CDATA[field_5670f2bfr2c5a]]></wp:meta_value>
</wp:postmeta>

<!-- 二つ目の画像 -->
<wp:postmeta>
    <wp:meta_key><![CDATA[images_1_image]]></wp:meta_key>
    <wp:meta_value><![CDATA[11]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
    <wp:meta_key><![CDATA[_images_1_image]]></wp:meta_key>
    <wp:meta_value><![CDATA[field_5670f2bad2c5a]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
    <wp:meta_key><![CDATA[images_1_caption]]></wp:meta_key>
    <wp:meta_value><![CDATA[Image 2]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
    <wp:meta_key><![CDATA[_images_1_caption]]></wp:meta_key>
    <wp:meta_value><![CDATA[field_5670f2bfr2c5a]]></wp:meta_value>
</wp:postmeta>
```


# 生成したXMLがうまくインポートされない時には

インポート処理は、*WordPress インポートツール*プラグインのコードを見ればわかる。
2ファイルしかないんで、XMLを試行錯誤してがんばるよりも、*WordPress インポートツール*プラグインのコードにデバッグコード入れて試した方が速いと思う。

ちなみに、インポート処理は、大まかに以下の流れみたい。

1. XMLをパースして、`authors`、`category`・`term`、`item`に分ける。
2. それぞれインポート
3. `item`の親子関係（記事と画像の関連付けなど）などを関連付ける

`item`は（多分、`category`とかも）、IDを見て同じものをインポートしないように制御してるんで、ちゃんとIDを一意にふっとかないと、何故かインポートされんなぁ、ってことになる。（はまった。）


# Python用のWordPress XML生成ライブラリ

仕事用に作ったPythonスクリプトをGitHubにあげてるんで、使えそうだったら使ってください。

[wordpress_importer_util](https://github.com/polikeiji/wordpress_importer_util)

日付周りの処理に[arrow](http://crsmithdev.com/arrow/)を使ってるんで、これだけ`pip install arrow`で入れてください。

`examples/my_wordpress.py`が、実際にXMLを生成するPythonスクリプトの例になってます。

なお、`category`や`term`は、実際に使わなかったんで、試してないです。
自己責任でご利用お願いします。