How LUXCM Handles Page Exports

Page selection and export — How to handle pages without categories

I deliberately did not make the “Select Pages” mode a copy of the “Select Posts” mode.

At first glance, both screens let you choose items with checkboxes. But pages are fundamentally different from posts.

Posts are grouped through a many-to-many classification system called categories. Pages, by contrast, have no concept of categories in the first place.

Instead, pages are organised through parent–child relationships—in other words, a hierarchy. If you ignore that distinction and migrate pages exactly like posts, the pages become scattered and lose the structure of the original site.

Pages: the “parent” is the site structure itself

In WordPress, posts are managed through a separate taxonomy called “categories,” while pages do not use that taxonomy.

Instead, each page can designate another page as its parent through the post_parent field. The resulting hierarchy is the site’s information architecture.

UIC’s guide to page hierarchy makes clear why this parent-page setting matters: assigning a parent page “automatically creates breadcrumb navigation” and provides editors with “authorial organisation in the back end.”

Put another way, when this hierarchy breaks, visitors lose the visual path that tells them where they are on the site, while administrators lose a logical way to group pages and content management becomes confusing.

That was when I realised that “Select Pages” could not be built using the same logic as “Select Posts by Category.”

Posts can be classified simply by assigning category tags, but for pages, the relationship itself—“what is this page’s parent?”—is part of the content.

If that relationship is not migrated, the imported pages may look fine individually, but together they become a collection of disconnected fragments with no clear place in the site.

I applied the lesson from comments directly to pages

In the first article in this series, I discussed WordPress Core Trac ticket #8019.

The issue was that, when importing comments, WordPress assigned new IDs to avoid collisions but failed to update the parent ID that identifies which comment each reply belonged to. As a result, reply threads were broken.

I knew that page hierarchies contained exactly the same trap. Suppose the “About Us” page has ID 12 on the source site, and its child page, “History,” is stored with post_parent = 12. When “About Us” is created on the destination site, however, it will receive an entirely different ID—not 12.

If “History” continues to point to the old ID, 12, that relationship becomes a dead reference pointing to no page on the new site.

That is why I divided page importing into two stages.

The parent is not assigned yet, because we do not know what new ID the parent page will receive on the destination site.

Once every page has been created, the second stage uses an “old ID → new ID” mapping table. Each page’s old parent ID is looked up in that table, replaced with the corresponding new ID, and then linked back to its parent.

This is exactly the same principle used to solve the problem addressed in Trac #8019 for comments: to migrate a relationship, you must assume that the IDs of the referenced objects will change, create a mapping table, and use it later to reconnect the relationships.

Rather than applying this principle to a single comment and stopping there, I applied it to page parent–child relationships as well.

One limitation I should state plainly

There is one limitation I should state plainly.

This mapping works only when the parent page is included in the same import.

If a user selects only the “History” page and leaves its parent, “About Us,” unselected, the mapping table contains no new ID corresponding to the old ID for “About Us.”

In that situation, “History” is created without its parent relationship restored, at the top level of the hierarchy.

That is not a bug; it is the only logical outcome. There is no way to connect a page to a parent that does not exist. Rather than treating this as an error and stopping the entire import, I let the page be created quietly as a top-level page.

But I want to be clear for anyone reading this series: when migrating hierarchical pages through “Select Pages,” you must select the parent pages along with the child pages for the structure to be preserved completely.

Menus are clearly a separate structure from pages

There is another distinction worth making.

A thread on the WordPress.org support forum illustrates it precisely.

The user had created child pages such as “Our School” and “Curriculum” under “About Us” and wanted the navigation menu to reproduce the same structure.

But the developer of the tool the user was using replied that “importing hierarchical menus—with parent and child menu items—is not supported by default.”

Migrating parent–child relationships between pages through the post_parent field and migrating the hierarchy of menu items registered in WordPress navigation menus (wp_nav_menu) involve different data.

I think it is important to explain this distinction accurately.

LUX Content Migration’s page-selection mode restores the parent–child relationship between pages—in other words, the post_parent field.

The menu must be configured manually—or configured again—by the user after the pages have been migrated to the destination site. I do not want to suggest that the plugin handles this part automatically.

To put the structure in order

Pages are a different kind of data from posts.

They are not classified with category tags; they form the site’s structure through parent–child relationships.

I did not ignore that distinction when building page selection.

I applied the same principle used to restore comment reply threads—linking old and new IDs through a mapping table and reconnecting relationships using that table after all items have been created—to page parent–child relationships as well.

That said, this approach depends on the parent page being included in the same import. It is also important to be clear that menu structures are entirely separate data from the page hierarchy, so the plugin does not restore them on your behalf.

I believe it is more useful to distinguish precisely between what the plugin can and cannot do—especially for anyone migrating a site with a real hierarchical structure.

References