Understanding and Applying Page Export Behavior

Selecting Pages for Export — How to Handle Pages Without Categories

I did not build “Select Pages” as a copy of “Select Posts.”

At a glance, both screens let you choose items with checkboxes. But pages have a fundamentally different structure from posts. Posts are organized through a many-to-many classification system called categories, while pages do not have categories at all.

Instead, pages are organized through parent-child relationships—in other words, a hierarchy.

If you ignore that distinction and move pages in exactly the same way as posts, the imported pages become scattered and lose the structure of the original site.

Pages: The “Parent” Is the Site Structure

In WordPress, posts are managed through a separate taxonomy called “categories,” but 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” in its most direct form.

UIC’s guide to page hierarchy makes clear why this parent-page setting matters: assigning a parent page “automatically creates breadcrumb navigation,” while also providing “authorial organization in the backend” for editors.

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, making content management itself confusing.

This was the point at which I confirmed that “Select Pages” could not use the same logic as “Select Posts by Category.”

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

If that relationship is not carried over, the imported pages may look fine individually, but as a site they become a collection of disconnected fragments with no clear sense of place.

I applied the lesson learned from comments directly to pages

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

The problem was that, when importing comments, WordPress assigned new IDs to avoid collisions but failed to update the parent ID that identifies which comment a reply belongs to. As a result, the reply structure was broken.

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

That is why I split page importing into two stages.

In the first stage, I create all the selected pages and record each page’s “old parent ID.”

I do not connect the parents yet—the parent page’s new ID is still unknown. Once every page has been created, the second stage uses an “old ID → new ID” mapping table. It looks up each page’s old parent ID, replaces it with the corresponding new ID, and then restores the relationship.

This is exactly the same principle and solution used for Trac #8019: to preserve a relationship, create a mapping table on the assumption that the ID of the referenced object will change, then reconnect the relationship using that table.

Rather than applying this principle only to comments, I applied it to page parent-child relationships as well.

One limitation I should state plainly

There is one limitation I want to state plainly: this mapping works only when the parent page is included in the same import operation.

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

In this case, the “History” page is left without its parent and becomes a top-level page. That is not a bug but the logical outcome—there is no way to connect it to a parent that does not exist.

Rather than treating this as an error and blocking the entire import, I let the page quietly become a top-level page.

For readers of this series, I want to make one point clear: when moving hierarchical pages with “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 clearly.

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

However, the developer of the tool the user was working with replied that “importing hierarchical menus (with parent and child menu items) is not supported by default.”

Moving the parent-child relationships between pages through the post_parent field and moving the hierarchy of menu items registered in WordPress’s navigation menus (wp_nav_menu) involve different sets of data.

I believe this distinction needs to be communicated clearly to users.

LUX Content Migration restores the parent-child relationships between pages—in other words, the post_parent field.

Once that relationship is restored, WordPress features that directly reference the page hierarchy, such as breadcrumbs and lists of a page’s child pages, continue to work as expected.

However, navigation menus that an administrator has manually built under Appearance → Menus are stored separately from the page hierarchy, so they fall outside the scope of this plugin.

After moving the pages to the destination site, the user must configure the menu themselves—or configure it again. I do not want to suggest that this plugin handles that part automatically.

Putting Page Export in Perspective

Pages are a different kind of data from posts.

They are not classified with category tags. Instead, parent-child relationships form the structure of the site itself.

I did not ignore this distinction and build the page-selection mode anyway. I applied to page parent-child relationships the same principle used to restore comment reply structures: connect old and new IDs through a mapping table, then use that table to restore relationships after every item has been created.

I believe it is more useful for people moving sites with hierarchical structures to understand precisely what this plugin can and cannot do.

References

This article has styling effects from DECOBLOCKS applied to it.