work with us

How to keep the relationship between two custom post types when moving content with the import/export tool in WordPress

Written by Andrea Ariu: Web developer.
· 4 minute read

The import/export tool for WordPress is widely used as we often need to move some posts from a website to another. When importing posts that belong to two different post types and have a relationship with each other, we could come across some issues on our way.

The relationships are usually defined by the post parent or a meta value, which refer to a post ID, therefore, if some IDs in the xml export file are already in use, WordPress will import the posts with different IDs that are still unused, and, consequently, the relationships will be lost.

Assuming that we have exported the posts belonging to two custom post types into the post_type_1.xml and post_type_2.xml files, we need to make sure that every ID in these files is not in use.

First of all, we need to find the highest ID in use by using the following code:

$post_id = $wpdb->get_results("SELECT MAX(ID) as max_id FROM $table_name")[0]->max_id;

We now know that every ID higher than that value is available.

Assuming that the post type 2 contains the actual relationship values, we need to go through every post in the post_type_2.xml file, and replace the post IDs that refer to the post type 1 with some available IDs.

Here we get the xml export files as strings.

$post_type_1_str = file_get_contents('post_type_1.xml');
$post_type_2_str = file_get_contents('post_type_2.xml');

Then, we get an object of class SimpleXMLElement for the post type 2.

$post_type_2_xml = simplexml_load_string($post_type_2_str);

We are now ready to go through every post.

foreach ($post_type_2_xml>channel->item as $item) {

  $wp = $item->children( 'wp', true );    

  ....

  $post_id++;

}

For every post we need to update the values that define the relationship. As already mentioned, they can be stored as either post parent or meta value. We will cover both cases.

Post parent:
We simply update the post parent with the new ID.

$old_id = $wp->post_parent;
$wp->post_parent = $post_id;

Meta value:
We find the meta value we need by meta key, called “_relationship_key” in the example, then we update that value with the new ID. Note that a meta value appears as Character Data (CDATA) in a xml, so we need to use the DOMElement object APIs, that allow us to operate with CDATA values.

foreach ( $wp->postmeta as $meta ) {

  if ((string) $meta->meta_key !== '_relationship_key') continue;                

  $old_id = (int) $meta->meta_value;                

  // Update CDATA
  $node = dom_import_simplexml($meta->meta_value); 
  $no   = $node->ownerDocument; 
  $node->nodeValue = '';
  $node->appendChild($no->createCDATASection($post_id)); 

  $wp->post_parent = $post_id;

}

As you can see, we have stored the original relationship values that refer to the post type 1, in the $old_id variable, as we still need to replace those values with the new IDs in the post_type_1.xml export file. To do that, we add the below code inside the loop:

$post_type_1_str = str_replace(''.$old_id.'', ''.$post_id.'', $post_type_1_str);

As last, outside the main loop, we can now save the export files updated with the new IDs.

file_put_contents('post_type_1.xml', $post_type_1_str);
file_put_contents('post_type_2.xml', $post_type_2_xml->saveXML());

This workaround suits the case of many plugins that don’t have their own import/export tool. One of them is DW Question & Answer, that builds a complete Question & Answer system similar to Stack Overflow. It relies on two custom post types that have a relationship, and are used to store the questions and answers. So, if you use this plugin and are struggling with the import, well, you can now put into practice the info in this blog.

If you’re looking for a professional web development agency for large web projects, contact RUN2 today.