Perform bulk updates of WordPress post content with PHP

Full source code on Github.

I was working on a WordPress website with hundreds of pages where almost every sentence needed editing. The website used the SiteOrigin Page Builder plugin. To do mutiple updates directly for each page in the WP admin console is a very time-consuming and tedious process. One has to access all the panels separately, it takes a lot of clicking, closing and saving.

I did some research but did not find an existing plugin that could make that job easier. Finally I ended up developing some PHP code myself that permits to process quickly a large batch of changes to multiple pages.

A very usefull plugin is the WP plugin Better Search And Replace (BSR) but it takes only one search term at a time. On the other hand the used code is very reliable and efficient. A major complication with search and replace operations in WordPress is that WP plugins often store data in a serialized form, but BSR handles this flawlessly.

I reused code from this plugin and build a PHP solution around it to enable batch processing of updates.

Several features have been added:

General functioning of the WP plugin Better Search And Replace

After installing the WP Better Search And Replace the code that actually permits a search and replace can be found in /wp-content/plugins/better-search-replace/includes/class-bsr-db.php This file contains a class BSR_DB, the essential code is in the member function recursive_unserialize_replace().

In the WP admin panel under the Tools menu one finds the BSR options page. If the user clicks the button Run Search/Replace [TODO image] the form is processed by class-bsr-ajax.php. The function process-search-replace() is called which instantiated a BSR_DB object and calls its method srdb() which actually does the search and replace in a given database table. A complication to reuse this method in our case is that the table is divided in pages and srdb() is called for each page. Several ajax calls are passed for each page to update progress info in the interface. Paging helps performance if one does a search in the complete database, especially if it is very large.

Adaptations made to enable bulk updates of content in WordPress posts.

In our case paging is not needed since we limit the search to the wp_content and wp_postmeta tables and limit it also to specific posts. We don’t implement a UI form, so we don’t need ajax either. The user is simply informed after the code has run by echoing a summary of the result to a web page. All details can be found in the log that is created for each run.

General outline of the bvs_update_posts PHP code.

The full source code can be found on Github.

First a CSV file needs to be created in which the revisions are listed, see this example for the correct syntax.

The file update_posts.php is run to start the updating of the web pages. This file will read the CSV and put the data in the array $csv_revisions:

		$csv_revisions= import_tab_csv_large(getcwd().'/'.$bvs_update_posts_args['csv_revision_info']);

The posts to update can be limited by setting $bvs_update_posts_args['query'] :

	'dry_run'=>'on',  //'on' or 'off'
	// Should be a valid query to select the posts to process.
	// Note in this case the use of $wpdb which is only available once the WP environment is set up.
	"SELECT * FROM $wpdb->posts WHERE 
			post_name LIKE 'cities-%' AND
			post_type='page' AND post_status='publish' ORDER BY post_name ASC",			
	'check_serialize_precision'=>true  //Check serialized double and float values, true/false

For each post the table wp_posts is searched and updated by calling the function bvs_update_table. This is a wrapper function in which BVS_BSR_DB::srdb() is called. Several extra arguments are added which are not present in the original BSR version. Note that a ['where'] argument permits to limit the search to a specific post ID. Also the search is limited to the column 'post_content'.

	$update_table_args['search_for']= empty($so_search) ? $search: $so_search;
	$update_table_args['replace_with']= empty($so_replace) ? $replace: $so_replace;
	$update_table_args['table']=$wpdb->prefix.'posts';//!!adding prefix is necessary

For each post the table wp_postmeta is also updated:

		//Update table _postmeta
		$update_table_args['table']=$wpdb->prefix.'postmeta';//!!adding prefix is necessary	

This is in particular essential if the SiteOrigin Page Builder is used since all page data are stored here in a rather complex serialized way. In this particular case all data connected to a page ID are updated. This means that e.g. SEO snippet preview for a page title will also be updated. One could limit the search to rows in which data for a specific plugin are stored by adapting the [‘where’] argument. As is also the case in the original BSR plugin a dry run is possible:

$update_table_args['dry_run']=$bvs_update_posts_args['dry_run']; //'on' or 'off'

The created log is much more detailed than the log the BSR plugin creates. This is especially important since many differerent updates in many pages can be done in a single run. One wants to be able to keep track easily of all these changes. The log also permits to establish easily if a search term was not found at all. This could indicate a problem with the search term and can so be corrected.

How to use the bvs_update_posts PHP code?