ページのエクスポート特性を理解して適用する

ページ選択によるエクスポート — カテゴリーのないページを扱う方法

私は「ページ選択」モードを、「投稿選択」モードの単なるコピーにはしなかった。

見た目はどちらもチェックボックスで項目を選ぶ画面だが、ページと投稿では構造が根本的に異なる。投稿はカテゴリーという多対多の分類体系でまとめられる一方、ページにはそもそもカテゴリーという概念がない。

その代わり、ページは親子関係、つまり階層構造によって整理される。

この違いを無視してページを投稿と同じ方法で移行すると、移行先のページがばらばらになり、元のサイト構造が失われてしまう。

ページでは「親」がそのままサイト構造になる

WordPressでは、投稿は「カテゴリー」という独立した分類体系で管理されるが、ページはこのtaxonomyを使わない。

その代わり、各ページはpost_parentフィールドによって別のページを親に指定できる。このようにして作られる上下関係が、そのサイトの「情報構造」そのものになる。

UICのページ階層に関する解説では、親ページの設定がなぜ重要なのかが明確に説明されている。親ページを指定すると「自動的にパンくずナビゲーションが生成され」、編集者にとっても「バックエンドでの編集上の整理」が実現するという。

逆に言えば、この階層構造が崩れると、訪問者は「サイト内のどこにいるのか」を示す視覚的な道筋を失い、管理者にとってもページ同士が論理的にまとまらないため、コンテンツ管理そのものが混乱する。

この時点で私は、「ページ選択」を「カテゴリー別の投稿選択」とまったく同じロジックで実装することはできないと確認した。

投稿はカテゴリーというタグを付けて整理するだけで分類が成り立つが、ページでは「このページの親は何か」という関係そのものがコンテンツの一部になる。

この関係まで移行できなければ、移行されたページは個別には問題なく見えても、サイト全体で見ると方向性を失った断片の集まりになってしまう。

コメントから得た教訓をページにもそのまま適用した

私はこのシリーズの最初の記事で、WordPressコアのTracチケット#8019を取り上げた。

コメントをインポートする際、WordPressはIDの衝突を避けるために新しいIDを割り当てる。しかし、「このコメントはどのコメントへの返信なのか」を示す親IDを更新しなかったため、返信構造が壊れてしまうという問題だった。

「沿革」ページが古いIDである12を親として保持したままだと、その関係は移行先サイトのどのページも指し示せない、無効な参照になってしまう。

そこで私は、ページのインポートを2段階に分けた。

第1段階では、選択されたすべてのページをいったん作成し、それぞれのページについて「古い親ID」を記録しておく。

この段階では、まだ親ページを関連付けない。親ページ自体が移行先サイトでどのIDを受け取るのか、まだ分からないからだ。すべてのページを作成し終えたら、第2段階で「古いID→新しいID」の対応表を参照し、各ページの古い親IDを新しいIDに置き換えて、改めて親子関係を結び直す。

これは、Trac #8019でコメントに適用した解決策と原理がまったく同じだ。関係を移行するには、その関係が参照する対象のIDが変わることを前提に対応表を作成し、後からその表を使って関係を結び直さなければならない。

私はこの原則をコメントだけに適用して終わらせず、ページの親子関係にも同じように適用した。

正直に伝えておくべき限界が一つある

ここで、隠さずに伝えておくべきことが一つある。この対応付けが成立するのは、「親ページも同じインポート作業に含まれている場合」に限られる。

もしユーザーが「沿革」ページだけを選択し、その親である「会社概要」ページを選択しなかった場合、「会社概要」の古いIDに対応する新しいIDは、対応表にそもそも存在しない。

この場合、「沿革」ページは親とのつながりが復元されないまま、階層の最上位(親なし)として作成される。これはバグではなく、論理的に当然の結果だ。存在しない親に接続する方法はないからである。

私はこれをエラーとして扱い、インポート全体を中止するのではなく、ページを静かに最上位階層へ移すようにした。

ただし、このシリーズを読む人には明確に伝えておきたい。階層構造を持つページを「ページ選択」モードで移行する場合、子ページだけでなく、その親ページも一緒に選択しなければ、構造全体は維持されない。

メニューはページとは別の構造であることも明確にしておく必要がある

もう一つ、指摘しておきたい点がある。WordPress.orgのサポートフォーラムにあるあるスレッドは、この違いを正確に示している。

このユーザーは、「会社概要」の下に「私たちの学校」や「カリキュラム」といった子ページを作り、ナビゲーションメニューも同じ構造にしたいと考えていた。

しかし、そのユーザーが使っていたツールの開発者は、「階層型メニュー(親項目と子項目を持つメニュー)のインポートは、標準ではサポートされていない」と回答した。

post_parentフィールドでページ間の親子関係を移行することと、WordPressのナビゲーションメニュー(wp_nav_menu)に登録されたメニュー項目の階層構造を移行することは、別のデータを扱う作業である。

私は、この違いをユーザーに正確に伝える必要があると考えている。

LUX Content Migrationのページ選択モードが復元するのは、ページそのものの親子関係、つまりpost_parentフィールドだ。

これが復元されれば、パンくずリストや「このページの子ページ一覧」のように、WordPressがページ階層を直接参照して生成する機能は正常に動作する。

ただし、管理者が「外観→メニュー」で手動構成したナビゲーションメニューは、ページ階層とは別のデータとして保存されるため、これはこのプラグインの対象外だ。

メニューは移行先サイトでページを移した後、ユーザーが自分で(または改めて)構成しなければならない。この部分まで、このプラグインがすべて処理してくれるかのように説明したくはない。

ページのエクスポートを整理すると

ページは、投稿とは異なる種類のデータだ。

カテゴリーというタグで分類されるのではなく、親子関係によってサイトそのものの構造を形作る。

私はこの違いを無視してページ選択モードを作ることはしなかった。コメントの返信構造を復元したときと同じ原理、つまり古いIDと新しいIDを対応表で結び、すべての項目が作成された後にその表を参照して関係を結び直す方法を、ページの親子関係にもそのまま適用した。

このプラグインでできることとできないことを正確に区別して伝えるほうが、実際に階層構造を持つサイトを移行しようとしている人にとって、より役立つと私は考えている。

参考資料

この記事にはDECOBLOCKSの装飾効果が適用されています。