WordPressには記事のインポートおよびエクスポートの機能があります。WordPress同士の場合はWordPress Importerというツールがあるので、これを使うのですが、いわゆる引越しやドメイン変更の場合などによく使われますね。
この手順についてはたくさんあるのですが、困るのが「サイトAとサイトBをマージしたい」という場合です。
たとえば、このサイトが高橋文樹.comだったとして、高橋文樹ニュースというサイトをやっていたとしましょう。
で、アクセスが少ないので、ニュースサイトを閉じましょうということになります。とはいえ、もったいないので、ニュースサイトの方の投稿のうち、「告知」と「メディア掲載情報」だけを高橋文樹.comのカスタム投稿タイプnewsにして、インポートしたいとかそういうことですね。
この場合、気をつけなくてはいけないのは以下の点です。
- すべての投稿タイプが必要とは限らず、いるものといらないものがある
- なんらかのカスタマイズを施しており、添付画像の投稿IDなどがカスタムフィールドに保存されている
ようするに、インポートするときにいろいろとカスタマイズしたいことがあるよね、というわけです。
WordPress ImporterのXMLをカスタマイズ
もしかしたらプラグインでそういうのがあるのかもしれませんが、WordPress Importerの吐き出すXMLをちょっとカスタマイズするだけでなんとかなったりするので、その方法を説明します。
PHPスクリプトを用意
変換はPHPスクリプトで行います。まず、converter.phpというファイルを用意して、ターミナルから php converter.phpj [元のファイルパス] [出力ファイルパス]
とか入力することで、求めるXMLを取得します。
このファイルの中身はこんな感じです。
<?php try{ // 引数のチェック if( count($argv) < 3){ throw new Exception(’Too few arguments.', 404); } $file = $argv[1]; if( !file_exists($file) ){ throw new Exception('XML File not found.', 404); } $new_file = $argv[2]; // XMLの読み込み $xml = simplexml_load_file($file); // インデックス $index++; // 削除すべきノードのインデックスを保存 $node_to_delete = array(); foreach( $xml->channel->item as $item ){ /** @var SimpleXMLElement $item */ // Do Stuff! echo '.’; // 終わったよという意味でピリオドを出力 $index++; } // 新しいXMLを保存 if( !$xml->saveXML($new_file) ){ throw new Exception(‘Sorry, but failed to save XML.’, 500); } // 名前空間が付いてしまうので、削除。 // ※ やり方がよくわからなかった。 file_put_contents($new_file, str_replace(' xmlns:wp="wp"', '', file_get_contents($new_file))); // 終わったよというメッセージを表示 echo PHP_EOL.sprintf('Saved as %s', $new_file).PHP_EOL; }catch ( Exception $e ){ // 例外が発生したらメッセージ表示 echo sprintf('[Error %s] %s', $e->getCode(), $e->getMessage()).PHP_EOL; } exit;
上記のコードのうち、”Do Stuff!”とかいてあるところで投稿を表現するXMLノード$item
に対して操作を行えばいいんですね。
具体的に何が必要かはケースバイケースなので、ありがちな操作を書いておきます。
投稿タイプを取得&変更
これは簡単です。postとpageおよびattachment(添付ファイル)以外の投稿タイプはいらないから削除し、ページを投稿にするケースを考えます。
// 投稿タイプの取得 $post_type = (string) $item->children('wp', true)->post_type; if( ‘page’ == $post_type ){ // 投稿タイプがpageだったら、投稿タイプを変更 $item->children('wp', true)->post_type = 'post'; }elseif( false === array_search($post_type, array(‘post’, ‘attachment')) ){ // 投稿タイプがattachmentおよびpostではなかったら削除キューに追加 array_unshift($node_to_delete, $index); }
ポイントとしては、SimpleXMLが名前空間の扱いで謎の挙動を見せるということでしょうか。
あと、要素の削除はunsetなのですが、 foreach内でやってしまうと、次のノードの取得に影響が出ます。イテレータ実装だからでしょう。
したがって、削除すべきノードの番号を一旦配列に保存しておいて、あとから削除します。番号を大きい順に保存したのは、削除によって対象ノードが変わってしまうからです。
foreach( $node_to_delete as $index){ // 削除 unset($xml->channel->item[$index]); }
ここら辺、もっとうまいやり方がありそうですが、とりあえずこんな感じで。
カスタムフィールドを追加
カスタムフィールドを追加するケースを考えてみましょう。今回は以前あった投稿IDをカスタムフィールドに保存しておくようにします。
// 投稿IDを取得 $post_id = (int) $item->children('wp', true)->post_id; // カスタムフィールド用のノードを追加 $pm = $item->addChild('wp:postmeta', '', 'wp'); $pm->addChild('wp:meta_key', '_old_post_id', 'wp'); $pm->addChild('wp:meta_value', $post_id, 'wp');
これで以前の投稿IDが_old_post_id
として保存されました。
こうして保存しておけば、上述した「添付ファイルのIDがカスタムフィールドでひも付けられている」というようなケースでも問題ないでしょう。
ちなみに、アイキャッチはカスタムフィールドに添付ファイルのIDが保存される仕組みだったと思いますが、その部分をWordPress Importerがケアしているのかどうかはよく知りません。知っている人はコメントください。
まとめ
というわけで、インポートする際にある程度事前処理をしておけば楽になります。移行後にバッチ処理を走らせる場合も、事前にカスタムフィールドなどでフラグを立てておけば楽になるでしょう。ポイントは……
- データの削除はちょっと面倒。更新と追加は割と簡単。
- とりあえずインポート元のデータは全部エクスポートしてしまい、それから加工すると楽。
- 添付画像の登録処理と紐付けは自分で作ると結構めんどくさいので、このツールに任せましょう。
はじめに紹介した例の他に、大人の事情でサーバ管理者と入稿者とWordPress開発者が分断されている場合、入稿の準備ができてるのにサーバの準備ができませんなどという面倒くさい事情も考えられます。そういうとき、便利ですね。終わり。
[429] [429] Client error: `POST https://webservices.amazon.co.jp/paapi5/getitems` resulted in a `429 Too Many Requests` response: {"__type":"com.amazon.paapi5#TooManyRequestsException","Errors":[{"Code":"TooManyRequests","Message":"The request was de (truncated...)