ページのエクスポート特性を理解して適用する
ページ選択によるエクスポート — カテゴリーのないページを扱う方法
私は「ページ選択」モードを、「投稿選択」モードの単なるコピーにはしなかった。
見た目はどちらもチェックボックスで項目を選ぶ画面だが、ページと投稿では構造が根本的に異なる。投稿はカテゴリーという多対多の分類体系でまとめられる一方、ページにはそもそもカテゴリーという概念がない。

その代わり、ページは親子関係、つまり階層構造によって整理される。
この違いを無視してページを投稿と同じ方法で移行すると、移行先のページがばらばらになり、元のサイト構造が失われてしまう。
ページでは「親」がそのままサイト構造になる
WordPressでは、投稿は「カテゴリー」という独立した分類体系で管理されるが、ページはこのtaxonomyを使わない。
その代わり、各ページはpost_parentフィールドによって別のページを親に指定できる。このようにして作られる上下関係が、そのサイトの「情報構造」そのものになる。
UICのページ階層に関する解説では、親ページの設定がなぜ重要なのかが明確に説明されている。親ページを指定すると「自動的にパンくずナビゲーションが生成され」、編集者にとっても「バックエンドでの編集上の整理」が実現するという。
この時点で私は、「ページ選択」を「カテゴリー別の投稿選択」とまったく同じロジックで実装することはできないと確認した。
この関係まで移行できなければ、移行されたページは個別には問題なく見えても、サイト全体で見ると方向性を失った断片の集まりになってしまう。
コメントから得た教訓をページにもそのまま適用した
私はこのシリーズの最初の記事で、WordPressコアのTracチケット#8019を取り上げた。
コメントをインポートする際、WordPressはIDの衝突を避けるために新しいIDを割り当てる。しかし、「このコメントはどのコメントへの返信なのか」を示す親IDを更新しなかったため、返信構造が壊れてしまうという問題だった。
私は、ページの親子関係にもまったく同じ落とし穴があることを知っていた。元のサイトで「会社概要」ページのIDが12で、その下にある「沿革」ページがpost_parent = 12として保存されていたとしても、移行先で「会社概要」ページが新たに作成されれば、そのIDは12ではなく、まったく別の番号になる。
「沿革」ページが古いIDである12を親として保持したままだと、その関係は移行先サイトのどのページも指し示せない、無効な参照になってしまう。

そこで私は、ページのインポートを2段階に分けた。
第1段階では、選択されたすべてのページをいったん作成し、それぞれのページについて「古い親ID」を記録しておく。
この段階では、まだ親ページを関連付けない。親ページ自体が移行先サイトでどのIDを受け取るのか、まだ分からないからだ。すべてのページを作成し終えたら、第2段階で「古いID→新しいID」の対応表を参照し、各ページの古い親IDを新しいIDに置き換えて、改めて親子関係を結び直す。
私はこの原則をコメントだけに適用して終わらせず、ページの親子関係にも同じように適用した。
正直に伝えておくべき限界が一つある
ここで、隠さずに伝えておくべきことが一つある。この対応付けが成立するのは、「親ページも同じインポート作業に含まれている場合」に限られる。
もしユーザーが「沿革」ページだけを選択し、その親である「会社概要」ページを選択しなかった場合、「会社概要」の古いIDに対応する新しいIDは、対応表にそもそも存在しない。
私はこれをエラーとして扱い、インポート全体を中止するのではなく、ページを静かに最上位階層へ移すようにした。
メニューはページとは別の構造であることも明確にしておく必要がある
もう一つ、指摘しておきたい点がある。WordPress.orgのサポートフォーラムにあるあるスレッドは、この違いを正確に示している。
このユーザーは、「会社概要」の下に「私たちの学校」や「カリキュラム」といった子ページを作り、ナビゲーションメニューも同じ構造にしたいと考えていた。
しかし、そのユーザーが使っていたツールの開発者は、「階層型メニュー(親項目と子項目を持つメニュー)のインポートは、標準ではサポートされていない」と回答した。
post_parentフィールドでページ間の親子関係を移行することと、WordPressのナビゲーションメニュー(wp_nav_menu)に登録されたメニュー項目の階層構造を移行することは、別のデータを扱う作業である。私は、この違いをユーザーに正確に伝える必要があると考えている。
LUX Content Migrationのページ選択モードが復元するのは、ページそのものの親子関係、つまりpost_parentフィールドだ。
これが復元されれば、パンくずリストや「このページの子ページ一覧」のように、WordPressがページ階層を直接参照して生成する機能は正常に動作する。
ただし、管理者が「外観→メニュー」で手動構成したナビゲーションメニューは、ページ階層とは別のデータとして保存されるため、これはこのプラグインの対象外だ。
ページのエクスポートを整理すると
ページは、投稿とは異なる種類のデータだ。
カテゴリーというタグで分類されるのではなく、親子関係によってサイトそのものの構造を形作る。
ただし、この方法が成立するには親ページも同じ作業に含まれていなければならない。また、メニュー構造はページ階層とはまったく別のデータであり、このプラグインが代わりに復元するものではないことも、明確にしておきたい。
このプラグインでできることとできないことを正確に区別して伝えるほうが、実際に階層構造を持つサイトを移行しようとしている人にとって、より役立つと私は考えている。
参考資料
- WordPress Core Tracチケット#8019 — WXRインポート時のコメントID処理エラーによるスレッド構造の喪失
- UIC RED — Importance of Parent Pages (Site Hierarchy)
- WordPress.orgサポートフォーラム — How would I import pages with parents pages and a menu?
