Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: logic func which will throw exception if core WP tables are empty. #330

Open
wants to merge 11 commits into
base: trunk
Choose a base branch
from
Open
56 changes: 49 additions & 7 deletions src/Command/General/ContentDiffMigrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
namespace NewspackCustomContentMigrator\Command\General;

use NewspackCustomContentMigrator\Command\InterfaceCommand;
use NewspackCustomContentMigrator\Exceptions\CoreWPTableEmptyException;
use NewspackCustomContentMigrator\Logic\ContentDiffMigrator as ContentDiffMigratorLogic;
use NewspackCustomContentMigrator\Utils\PHP as PHPUtil;
use RuntimeException;
use WP_CLI;

/**
Expand Down Expand Up @@ -385,7 +387,7 @@ public function cmd_search_new_content_on_live( $args, $assoc_args ) {
global $wpdb;
try {
$this->validate_db_tables( $live_table_prefix, [ 'options' ] );
} catch ( \RuntimeException $e ) {
} catch ( RuntimeException $e ) {
WP_CLI::warning( $e->getMessage() );
WP_CLI::line( "Now running command `newspack-content-migrator correct-collations-for-live-wp-tables --live-table-prefix={$live_table_prefix} --mode=generous --skip-tables=options` ..." );
$this->cmd_correct_collations_for_live_wp_tables(
Expand All @@ -396,6 +398,16 @@ public function cmd_search_new_content_on_live( $args, $assoc_args ) {
'skip-tables' => 'options',
]
);
} catch ( CoreWPTableEmptyException $e ) {
// If one of the empty tables is wp_links, we can ignore it. Add any other tables we can ignore to the array below.
$ignore_empty_tables = [
$live_table_prefix . 'links',
$live_table_prefix . 'termmeta',
$live_table_prefix . 'comments',
$live_table_prefix . 'commentmeta',
];

$this->handle_core_wp_table_empty_exception( $e, $ignore_empty_tables );
}

// Search distinct Post types in live DB.
Expand Down Expand Up @@ -500,7 +512,7 @@ public function cmd_migrate_live_content( $args, $assoc_args ) {
// Validate DBs.
try {
$this->validate_db_tables( $live_table_prefix, [ 'options' ] );
} catch ( \RuntimeException $e ) {
} catch ( RuntimeException $e ) {
WP_CLI::warning( $e->getMessage() );
WP_CLI::line( "Now running command `newspack-content-migrator correct-collations-for-live-wp-tables --live-table-prefix={$live_table_prefix} --mode=generous --skip-tables=options` ..." );
$this->cmd_correct_collations_for_live_wp_tables(
Expand All @@ -511,6 +523,16 @@ public function cmd_migrate_live_content( $args, $assoc_args ) {
'skip-tables' => 'options',
]
);
} catch ( CoreWPTableEmptyException $e ) {
// If one of the empty tables is wp_links, we can ignore it. Add any other tables we can ignore to the array below.
$ignore_empty_tables = [
$live_table_prefix . 'links',
$live_table_prefix . 'termmeta',
$live_table_prefix . 'comments',
$live_table_prefix . 'commentmeta',
];

$this->handle_core_wp_table_empty_exception( $e, $ignore_empty_tables );
}

// Set constants.
Expand Down Expand Up @@ -1157,7 +1179,7 @@ public function cmd_correct_collations_for_live_wp_tables( $args, $assoc_args )
* @param string $where_operand Search operand, can be '==' or '!='.
* @param bool $return_first If true, return just the first matched entry, otherwise returns all matched entries.
*
* @throws \RuntimeException In case an unsupported $where_operand was given.
* @throws RuntimeException In case an unsupported $where_operand was given.
*
* @return array Found results. Mind that if $return_first is true, it will return a one-dimensional array,
* and if $return_first is false, it will return two-dimensional array with all matched elements as subarrays.
Expand All @@ -1168,7 +1190,7 @@ private function filter_imported_posts_log( array $imported_posts_log_data, stri

// Validate $where_operand.
if ( ! in_array( $where_operand, $supported_where_operands ) ) {
throw new \RuntimeException( sprintf( 'Where operand %s is not supported.', $where_operand ) );
throw new RuntimeException( sprintf( 'Where operand %s is not supported.', $where_operand ) );
}

foreach ( $imported_posts_log_data as $entry ) {
Expand Down Expand Up @@ -1286,21 +1308,41 @@ private function get_data_from_log( $log, $json_keys ) {
return $data;
}

/**
* Handles CoreWPTableEmptyException.
*
* @param CoreWPTableEmptyException $e Exception object which specifies which tables are empty.
* @param array $ignore_empty_tables Array of tables to ignore if empty.
*
* @return void
*/
private function handle_core_wp_table_empty_exception( CoreWPTableEmptyException $e, array $ignore_empty_tables ) {
$empty_tables = array_diff( $e->get_tables(), $ignore_empty_tables );
if ( empty( $empty_tables ) ) {
WP_CLI::warning( sprintf( 'One, or all, of the following tables may be empty and will be ignored: %s', implode( $ignore_empty_tables ) ) );
} else {
WP_CLI::error( sprintf( 'In order for the content diff operation to proceed, the following empty tables must be addressed: %s', implode( $empty_tables ) ) );
}
}

/**
* Validates DB tables.
*
* @param string $live_table_prefix Live table prefix.
* @param array $skip_tables Core WP DB tables to skip (without prefix).
*
* @throws \RuntimeException In case that table collations do not match.
*
* @return void
* @throws RuntimeException In case that table collations do not match.
* @throws CoreWPTableEmptyException In case that some of the core WP tables are empty.
*/
public function validate_db_tables( string $live_table_prefix, array $skip_tables ): void {
self::$logic->validate_core_wp_db_tables_exist_in_db( $live_table_prefix, $skip_tables );

if ( ! self::$logic->are_table_collations_matching( $live_table_prefix, $skip_tables ) ) {
throw new \RuntimeException( 'Table collations do not match for some (or all) WP tables.' );
throw new RuntimeException( 'Table collations do not match for some (or all) WP tables.' );
}

self::$logic->validate_core_wp_db_tables_are_not_empty( $live_table_prefix, $skip_tables );
}

/**
Expand Down
44 changes: 44 additions & 0 deletions src/Exceptions/CoreWPTableEmptyException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php
/**
* CoreWPTableEmptyException class.
*
* @package NewspackCustomContentMigrator\Exceptions
*/

namespace NewspackCustomContentMigrator\Exceptions;

/**
* Class CoreWPTableEmptyException.
*
* This class facilitates the handling of instances where core WP tables are empty. It might be necessary
* to ignore certain tables even if they are empty. This class will allow us to obtain that list of
* empty core tables and allow us to make a decision on a case-by-case basis how to proceed.
*/
class CoreWPTableEmptyException extends \Exception {

/**
* Array of table names that are empty.
*
* @var string[] $tables Names of empty tables.
*/
protected array $tables = [];

/**
* Constructor.
*
* @param string[] $tables Table names.
*/
public function __construct( array $tables ) {
$this->tables = $tables;
parent::__construct( sprintf( 'The following core WP Tables are empty: %s', implode( $this->tables ) ) );
}

/**
* Returns the names of the empty tables.
*
* @return string[]
*/
public function get_tables(): array {
return $this->tables;
}
}
34 changes: 34 additions & 0 deletions src/Logic/ContentDiffMigrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace NewspackCustomContentMigrator\Logic;

use NewspackCustomContentMigrator\Exceptions\CoreWPTableEmptyException;
use \WP_CLI;
use \WP_User;
use NewspackContentConverter\ContentPatcher\ElementManipulators\WpBlockManipulator;
Expand Down Expand Up @@ -3181,6 +3182,39 @@ public function copy_table_data_using_proper_collation( string $prefix, string $
}
}

/**
* This function will ensure that the core WP DB tables are not empty. This is especially important
* for tables like wp_terms, wp_term_taxonomy, and wp_term_relationships. If these tables are empty,
* the site's taxonomies will not be recreated, and no terms will be associated with posts.
*
* @param string $table_prefix Table prefix.
* @param string[] $skip_tables Core WP DB tables to skip (without prefix).
*
* @return void
* @throws CoreWPTableEmptyException
*/
public function validate_core_wp_db_tables_are_not_empty( string $table_prefix, array $skip_tables = [] )
{
$empty_tables = [];

foreach ( self::CORE_WP_TABLES as $core_table ) {
if ( in_array( $core_table, $skip_tables ) ) {
continue;
}

$table_name = $table_prefix . $core_table;
$count = intval( $this->wpdb->get_var( "SELECT COUNT(*) FROM $table_name" ) );

if ( 0 === $count ) {
$empty_tables[] = $table_name;
}
}

if ( ! empty( $empty_tables ) ) {
throw new CoreWPTableEmptyException( $empty_tables );
}
}

/**
* Wrapper for WP's native \get_user_by(), for easier testing.
*
Expand Down