hardening 2.0: 2meet-data-optimizer (staged rollout, dev22 first) #3
@@ -860,23 +860,7 @@ class TMDO_CLI_Post {
|
|||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
private static function group_post_type_for_cli( string $group ): ?string {
|
private static function group_post_type_for_cli( string $group ): ?string {
|
||||||
switch ( $group ) {
|
return TMDO_Post_Fields::post_type_for_group( $group );
|
||||||
case 'attachment':
|
|
||||||
return 'attachment';
|
|
||||||
case 'wc_product':
|
|
||||||
return 'product';
|
|
||||||
case 'hp_listing_core':
|
|
||||||
return 'hp_listing';
|
|
||||||
case 'hp_request_core':
|
|
||||||
return 'hp_request';
|
|
||||||
case 'hp_vendor_core':
|
|
||||||
return 'hp_vendor';
|
|
||||||
case 'nav_menu_item':
|
|
||||||
return 'nav_menu_item';
|
|
||||||
case 'wp_core':
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ class TMDO_CLI_Term_Comment {
|
|||||||
WP_CLI::log( sprintf( 'Mode: %s', $term_mode ) );
|
WP_CLI::log( sprintf( 'Mode: %s', $term_mode ) );
|
||||||
WP_CLI::log( sprintf( 'wp_terms: %s', number_format_i18n( $terms_count ) ) );
|
WP_CLI::log( sprintf( 'wp_terms: %s', number_format_i18n( $terms_count ) ) );
|
||||||
WP_CLI::log( sprintf( 'wp_termmeta: %s (1:%s)', number_format_i18n( $termmeta_count ), $terms_count > 0 ? round( $termmeta_count / $terms_count, 2 ) : 'n/a' ) );
|
WP_CLI::log( sprintf( 'wp_termmeta: %s (1:%s)', number_format_i18n( $termmeta_count ), $terms_count > 0 ? round( $termmeta_count / $terms_count, 2 ) : 'n/a' ) );
|
||||||
self::print_flat_row( $wpdb->prefix . 'wpdo_term_hp_taxonomy', 'wpdo_term_hp_taxonomy' );
|
self::print_group_rows( 'term' );
|
||||||
self::print_flat_row( $wpdb->prefix . 'wpdo_term_misc', 'wpdo_term_misc' );
|
self::print_flat_row( $wpdb->prefix . 'wpdo_term_misc', 'wpdo_term_misc' );
|
||||||
|
|
||||||
WP_CLI::log( '' );
|
WP_CLI::log( '' );
|
||||||
@@ -437,7 +437,7 @@ class TMDO_CLI_Term_Comment {
|
|||||||
WP_CLI::log( sprintf( 'Mode: %s', $comment_mode ) );
|
WP_CLI::log( sprintf( 'Mode: %s', $comment_mode ) );
|
||||||
WP_CLI::log( sprintf( 'wp_comments: %s', number_format_i18n( $comments_count ) ) );
|
WP_CLI::log( sprintf( 'wp_comments: %s', number_format_i18n( $comments_count ) ) );
|
||||||
WP_CLI::log( sprintf( 'wp_commentmeta: %s (1:%s)', number_format_i18n( $commentmeta_count ), $comments_count > 0 ? round( $commentmeta_count / $comments_count, 2 ) : 'n/a' ) );
|
WP_CLI::log( sprintf( 'wp_commentmeta: %s (1:%s)', number_format_i18n( $commentmeta_count ), $comments_count > 0 ? round( $commentmeta_count / $comments_count, 2 ) : 'n/a' ) );
|
||||||
self::print_flat_row( $wpdb->prefix . 'wpdo_comment_hp_review', 'wpdo_comment_hp_review' );
|
self::print_group_rows( 'comment' );
|
||||||
self::print_flat_row( $wpdb->prefix . 'wpdo_comment_misc', 'wpdo_comment_misc' );
|
self::print_flat_row( $wpdb->prefix . 'wpdo_comment_misc', 'wpdo_comment_misc' );
|
||||||
|
|
||||||
WP_CLI::log( '' );
|
WP_CLI::log( '' );
|
||||||
@@ -470,6 +470,21 @@ class TMDO_CLI_Term_Comment {
|
|||||||
return 'Both entities at disabled. Promote to dual_write first to enable Hook Bus interception.';
|
return 'Both entities at disabled. Promote to dual_write first to enable Hook Bus interception.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the row count of every registered group table of an entity.
|
||||||
|
*
|
||||||
|
* @param string $entity_type 'term' or 'comment'.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function print_group_rows( string $entity_type ): void {
|
||||||
|
foreach ( TMDO_Term_Comment_Backfill::registered_groups() as $group => $type ) {
|
||||||
|
if ( $type === $entity_type ) {
|
||||||
|
$table = TMDO_Schema_Manager::get_table_name( $entity_type, $group );
|
||||||
|
self::print_flat_row( $table, TMDO_TABLE_PREFIX . $entity_type . '_' . $group );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print a flat table's row count (or "(missing)" when not present).
|
* Print a flat table's row count (or "(missing)" when not present).
|
||||||
*
|
*
|
||||||
@@ -647,7 +662,8 @@ class TMDO_CLI_Term_Comment {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$valid_groups = array( 'all', 'hp_taxonomy', 'hp_review' );
|
$registered = TMDO_Term_Comment_Backfill::registered_groups();
|
||||||
|
$valid_groups = array_merge( array( 'all' ), array_keys( $registered ) );
|
||||||
if ( ! in_array( $group, $valid_groups, true ) ) {
|
if ( ! in_array( $group, $valid_groups, true ) ) {
|
||||||
WP_CLI::error( 'Invalid --group. Choose: ' . implode( ', ', $valid_groups ) );
|
WP_CLI::error( 'Invalid --group. Choose: ' . implode( ', ', $valid_groups ) );
|
||||||
}
|
}
|
||||||
@@ -656,8 +672,7 @@ class TMDO_CLI_Term_Comment {
|
|||||||
if ( 'all' === $group ) {
|
if ( 'all' === $group ) {
|
||||||
$results = TMDO_Term_Comment_Backfill::backfill_all( $dry_run );
|
$results = TMDO_Term_Comment_Backfill::backfill_all( $dry_run );
|
||||||
} else {
|
} else {
|
||||||
$entity_type = 'hp_review' === $group ? 'comment' : 'term';
|
$results = array( $group => TMDO_Term_Comment_Backfill::backfill_group( $registered[ $group ], $group, $dry_run ) );
|
||||||
$results = array( $group => TMDO_Term_Comment_Backfill::backfill_group( $entity_type, $group, $dry_run ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render table.
|
// Render table.
|
||||||
|
|||||||
@@ -843,18 +843,11 @@ class TMDO_Installer {
|
|||||||
$wpdb->prefix . 'wpdo_user_core_profile',
|
$wpdb->prefix . 'wpdo_user_core_profile',
|
||||||
$wpdb->prefix . 'wpdo_user_social',
|
$wpdb->prefix . 'wpdo_user_social',
|
||||||
$wpdb->prefix . 'wpdo_user_commerce',
|
$wpdb->prefix . 'wpdo_user_commerce',
|
||||||
$wpdb->prefix . 'wpdo_user_hp_user',
|
|
||||||
$wpdb->prefix . 'wpdo_user_admin_prefs',
|
$wpdb->prefix . 'wpdo_user_admin_prefs',
|
||||||
$wpdb->prefix . 'wpdo_post_wp_core',
|
$wpdb->prefix . 'wpdo_post_wp_core',
|
||||||
$wpdb->prefix . 'wpdo_post_attachment',
|
$wpdb->prefix . 'wpdo_post_attachment',
|
||||||
$wpdb->prefix . 'wpdo_post_wc_product',
|
$wpdb->prefix . 'wpdo_post_wc_product',
|
||||||
$wpdb->prefix . 'wpdo_post_hp_listing_core',
|
|
||||||
$wpdb->prefix . 'wpdo_post_hp_request_core',
|
|
||||||
$wpdb->prefix . 'wpdo_post_hp_vendor_core',
|
|
||||||
$wpdb->prefix . 'wpdo_post_nav_menu_item',
|
$wpdb->prefix . 'wpdo_post_nav_menu_item',
|
||||||
$wpdb->prefix . 'wpdo_hot_hp_listing',
|
|
||||||
$wpdb->prefix . 'wpdo_term_hp_taxonomy',
|
|
||||||
$wpdb->prefix . 'wpdo_comment_hp_review',
|
|
||||||
$wpdb->prefix . 'wpdo_term_misc',
|
$wpdb->prefix . 'wpdo_term_misc',
|
||||||
$wpdb->prefix . 'wpdo_comment_misc',
|
$wpdb->prefix . 'wpdo_comment_misc',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -303,23 +303,7 @@ final class TMDO_Post_Shadow_Verifier {
|
|||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
private static function group_post_type( string $group ): ?string {
|
private static function group_post_type( string $group ): ?string {
|
||||||
switch ( $group ) {
|
return TMDO_Post_Fields::post_type_for_group( $group );
|
||||||
case 'attachment':
|
|
||||||
return 'attachment';
|
|
||||||
case 'wc_product':
|
|
||||||
return 'product';
|
|
||||||
case 'hp_listing_core':
|
|
||||||
return 'hp_listing';
|
|
||||||
case 'hp_request_core':
|
|
||||||
return 'hp_request';
|
|
||||||
case 'hp_vendor_core':
|
|
||||||
return 'hp_vendor';
|
|
||||||
case 'nav_menu_item':
|
|
||||||
return 'nav_menu_item';
|
|
||||||
case 'wp_core':
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -529,7 +529,7 @@ class TMDO_REST_API {
|
|||||||
* @return WP_REST_Response REST response.
|
* @return WP_REST_Response REST response.
|
||||||
*/
|
*/
|
||||||
public function get_listings( WP_REST_Request $request ): WP_REST_Response {
|
public function get_listings( WP_REST_Request $request ): WP_REST_Response {
|
||||||
$post_type = $request->get_param( 'post_type' ) ?? 'hp_listing';
|
$post_type = $request->get_param( 'post_type' ) ?? self::default_post_type();
|
||||||
if ( ! post_type_exists( $post_type ) || ! is_post_type_viewable( $post_type ) ) {
|
if ( ! post_type_exists( $post_type ) || ! is_post_type_viewable( $post_type ) ) {
|
||||||
return new WP_REST_Response(
|
return new WP_REST_Response(
|
||||||
array(
|
array(
|
||||||
@@ -1550,13 +1550,25 @@ class TMDO_REST_API {
|
|||||||
return $filters;
|
return $filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post type GET /listings serves when the request names none.
|
||||||
|
*
|
||||||
|
* Filter `tmdo_rest_default_post_type` lets an integration (e.g. the
|
||||||
|
* HivePress AddOn) make its own post type the default.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function default_post_type(): string {
|
||||||
|
return (string) apply_filters( 'tmdo_rest_default_post_type', 'post' );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Argument definitions for GET /listings.
|
* Argument definitions for GET /listings.
|
||||||
*/
|
*/
|
||||||
private function listings_args(): array {
|
private function listings_args(): array {
|
||||||
return array(
|
return array(
|
||||||
'post_type' => array(
|
'post_type' => array(
|
||||||
'default' => 'hp_listing',
|
'default' => self::default_post_type(),
|
||||||
'sanitize_callback' => 'sanitize_key',
|
'sanitize_callback' => 'sanitize_key',
|
||||||
),
|
),
|
||||||
'per_page' => array(
|
'per_page' => array(
|
||||||
|
|||||||
@@ -60,20 +60,22 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
final class TMDO_Term_Comment_Backfill {
|
final class TMDO_Term_Comment_Backfill {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Group → flat table suffix mapping. Mirror of TMDO_Hivepress_Term_Comment_Fields.
|
* Term and comment groups registered in the entity registry.
|
||||||
*
|
*
|
||||||
* @var array<string, array{entity_type:string, table_suffix:string}>
|
* @return array<string, string> Group name → entity type ('term' | 'comment').
|
||||||
*/
|
*/
|
||||||
private const GROUP_MAP = array(
|
public static function registered_groups(): array {
|
||||||
'hp_taxonomy' => array(
|
$groups = array();
|
||||||
'entity_type' => 'term',
|
if ( ! class_exists( 'TMDO_Entity_Registry' ) ) {
|
||||||
'table_suffix' => 'wpdo_term_hp_taxonomy',
|
return $groups;
|
||||||
),
|
}
|
||||||
'hp_review' => array(
|
foreach ( array( 'term', 'comment' ) as $entity_type ) {
|
||||||
'entity_type' => 'comment',
|
foreach ( TMDO_Entity_Registry::get_groups_for_type( $entity_type ) as $group ) {
|
||||||
'table_suffix' => 'wpdo_comment_hp_review',
|
$groups[ $group ] = $groups[ $group ] ?? $entity_type;
|
||||||
),
|
}
|
||||||
);
|
}
|
||||||
|
return $groups;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run backfill for every registered term + comment group.
|
* Run backfill for every registered term + comment group.
|
||||||
@@ -83,8 +85,8 @@ final class TMDO_Term_Comment_Backfill {
|
|||||||
*/
|
*/
|
||||||
public static function backfill_all( bool $dry_run = false ): array {
|
public static function backfill_all( bool $dry_run = false ): array {
|
||||||
$results = array();
|
$results = array();
|
||||||
foreach ( self::GROUP_MAP as $group_name => $cfg ) {
|
foreach ( self::registered_groups() as $group_name => $entity_type ) {
|
||||||
$results[ $group_name ] = self::backfill_group( $cfg['entity_type'], $group_name, $dry_run );
|
$results[ $group_name ] = self::backfill_group( $entity_type, $group_name, $dry_run );
|
||||||
}
|
}
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
@@ -110,7 +112,7 @@ final class TMDO_Term_Comment_Backfill {
|
|||||||
$result['error'] = 'Entity Registry unavailable';
|
$result['error'] = 'Entity Registry unavailable';
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
if ( ! isset( self::GROUP_MAP[ $group_name ] ) ) {
|
if ( ( self::registered_groups()[ $group_name ] ?? null ) !== $entity_type ) {
|
||||||
$result['error'] = "Unknown group: {$group_name}";
|
$result['error'] = "Unknown group: {$group_name}";
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
@@ -140,7 +142,7 @@ final class TMDO_Term_Comment_Backfill {
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
$flat_table = $wpdb->prefix . self::GROUP_MAP[ $group_name ]['table_suffix'];
|
$flat_table = TMDO_Schema_Manager::get_table_name( $entity_type, $group_name );
|
||||||
|
|
||||||
// Count candidate entities — those with at least one of the registered keys in wp_*meta.
|
// Count candidate entities — those with at least one of the registered keys in wp_*meta.
|
||||||
$placeholders = implode( ',', array_fill( 0, count( $keys ), '%s' ) );
|
$placeholders = implode( ',', array_fill( 0, count( $keys ), '%s' ) );
|
||||||
|
|||||||
@@ -41,23 +41,6 @@ final class TMDO_Term_Comment_Shadow_Verifier {
|
|||||||
/** Default sample size per entity per cron tick. */
|
/** Default sample size per entity per cron tick. */
|
||||||
public const DEFAULT_SAMPLE_SIZE = 100;
|
public const DEFAULT_SAMPLE_SIZE = 100;
|
||||||
|
|
||||||
/**
|
|
||||||
* Group → flat table suffix mapping. Keep in sync with
|
|
||||||
* TMDO_Hivepress_Term_Comment_Fields registrations and the misc bucket.
|
|
||||||
*
|
|
||||||
* @var array<string, array{entity_type:string, table_suffix:string, has_misc:bool}>
|
|
||||||
*/
|
|
||||||
private const GROUP_MAP = array(
|
|
||||||
'hp_taxonomy' => array(
|
|
||||||
'entity_type' => 'term',
|
|
||||||
'table_suffix' => 'wpdo_term_hp_taxonomy',
|
|
||||||
),
|
|
||||||
'hp_review' => array(
|
|
||||||
'entity_type' => 'comment',
|
|
||||||
'table_suffix' => 'wpdo_comment_hp_review',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a sample-compare pass for a single entity group.
|
* Run a sample-compare pass for a single entity group.
|
||||||
*
|
*
|
||||||
@@ -215,11 +198,7 @@ final class TMDO_Term_Comment_Shadow_Verifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
global $wpdb;
|
foreach ( TMDO_Term_Comment_Backfill::registered_groups() as $group => $entity_type ) {
|
||||||
|
|
||||||
foreach ( self::GROUP_MAP as $group => $cfg ) {
|
|
||||||
$entity_type = $cfg['entity_type'];
|
|
||||||
|
|
||||||
// Only run when the entity is in shadow_read mode.
|
// Only run when the entity is in shadow_read mode.
|
||||||
if ( 'term' === $entity_type && ! $term_shadow ) {
|
if ( 'term' === $entity_type && ! $term_shadow ) {
|
||||||
continue;
|
continue;
|
||||||
@@ -232,7 +211,7 @@ final class TMDO_Term_Comment_Shadow_Verifier {
|
|||||||
if ( empty( $keys ) ) {
|
if ( empty( $keys ) ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$flat_table = $wpdb->prefix . $cfg['table_suffix'];
|
$flat_table = TMDO_Schema_Manager::get_table_name( $entity_type, $group );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
self::sample_compare( $entity_type, $group, $flat_table, $keys, self::DEFAULT_SAMPLE_SIZE );
|
self::sample_compare( $entity_type, $group, $flat_table, $keys, self::DEFAULT_SAMPLE_SIZE );
|
||||||
@@ -337,16 +316,14 @@ final class TMDO_Term_Comment_Shadow_Verifier {
|
|||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
global $wpdb;
|
|
||||||
$out = array();
|
$out = array();
|
||||||
|
|
||||||
foreach ( self::GROUP_MAP as $group => $cfg ) {
|
foreach ( TMDO_Term_Comment_Backfill::registered_groups() as $group => $entity_type ) {
|
||||||
$entity_type = $cfg['entity_type'];
|
$keys = TMDO_Entity_Registry::get_group_keys( $entity_type, $group );
|
||||||
$keys = TMDO_Entity_Registry::get_group_keys( $entity_type, $group );
|
|
||||||
if ( empty( $keys ) ) {
|
if ( empty( $keys ) ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$flat_table = $wpdb->prefix . $cfg['table_suffix'];
|
$flat_table = TMDO_Schema_Manager::get_table_name( $entity_type, $group );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$out[ $group ] = self::sample_compare( $entity_type, $group, $flat_table, $keys, $sample_size );
|
$out[ $group ] = self::sample_compare( $entity_type, $group, $flat_table, $keys, $sample_size );
|
||||||
|
|||||||
@@ -43,6 +43,46 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
*/
|
*/
|
||||||
final class TMDO_Post_Fields {
|
final class TMDO_Post_Fields {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post type of each core group that is not scoped to post types in the
|
||||||
|
* registry (wp_core spans every post type and is absent on purpose).
|
||||||
|
*/
|
||||||
|
private const GROUP_POST_TYPES = array(
|
||||||
|
'attachment' => 'attachment',
|
||||||
|
'wc_product' => 'product',
|
||||||
|
'nav_menu_item' => 'nav_menu_item',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary post type of a post group: the registry scope for scoped groups
|
||||||
|
* (e.g. the HivePress AddOn's), the core mapping otherwise.
|
||||||
|
*
|
||||||
|
* @param string $group Entity group name.
|
||||||
|
* @return string|null Null for cross-post-type groups such as wp_core.
|
||||||
|
*/
|
||||||
|
public static function post_type_for_group( string $group ): ?string {
|
||||||
|
$scoped = class_exists( 'TMDO_Entity_Registry' ) ? TMDO_Entity_Registry::get_group_post_types( 'post', $group ) : array();
|
||||||
|
return $scoped[0] ?? ( self::GROUP_POST_TYPES[ $group ] ?? null );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post groups dedicated to a post type (wp_core excluded).
|
||||||
|
*
|
||||||
|
* @param string $post_type Post type slug.
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public static function groups_for_post_type( string $post_type ): array {
|
||||||
|
$groups = array_keys( self::GROUP_POST_TYPES, $post_type, true );
|
||||||
|
if ( class_exists( 'TMDO_Entity_Registry' ) ) {
|
||||||
|
foreach ( TMDO_Entity_Registry::get_groups_for_type( 'post' ) as $group ) {
|
||||||
|
if ( in_array( $post_type, TMDO_Entity_Registry::get_group_post_types( 'post', $group ), true ) ) {
|
||||||
|
$groups[] = $group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array_values( array_unique( $groups ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook into WPDO entity field registration.
|
* Hook into WPDO entity field registration.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -546,8 +546,8 @@ final class TMDO_Post_Migration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map group name to the post_type it targets. The mapping is canonical
|
* Map group name to the post_type it targets (see
|
||||||
* to v2.9.1 group definitions in TMDO_Post_Fields.
|
* TMDO_Post_Fields::post_type_for_group()).
|
||||||
*
|
*
|
||||||
* Returns null for cross-post_type groups (e.g. 'wp_core').
|
* Returns null for cross-post_type groups (e.g. 'wp_core').
|
||||||
*
|
*
|
||||||
@@ -555,23 +555,7 @@ final class TMDO_Post_Migration {
|
|||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
private static function group_post_type( string $group ): ?string {
|
private static function group_post_type( string $group ): ?string {
|
||||||
switch ( $group ) {
|
return TMDO_Post_Fields::post_type_for_group( $group );
|
||||||
case 'attachment':
|
|
||||||
return 'attachment';
|
|
||||||
case 'wc_product':
|
|
||||||
return 'product';
|
|
||||||
case 'hp_listing_core':
|
|
||||||
return 'hp_listing';
|
|
||||||
case 'hp_request_core':
|
|
||||||
return 'hp_request';
|
|
||||||
case 'hp_vendor_core':
|
|
||||||
return 'hp_vendor';
|
|
||||||
case 'nav_menu_item':
|
|
||||||
return 'nav_menu_item';
|
|
||||||
case 'wp_core':
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -43,23 +43,6 @@ class TMDO_Post_Query_Router {
|
|||||||
/** Query var key for capturing routed clauses (consumed by posts_join/where). */
|
/** Query var key for capturing routed clauses (consumed by posts_join/where). */
|
||||||
private const QUERY_VAR = 'wpdo_post_clauses';
|
private const QUERY_VAR = 'wpdo_post_clauses';
|
||||||
|
|
||||||
/**
|
|
||||||
* Map post_type → entity group (mirror of TMDO_Post_Migration::group_post_type
|
|
||||||
* but inverted; a post_type may map to multiple groups eventually, currently
|
|
||||||
* we route to the most-specific *_core group plus the cross-cutting wp_core
|
|
||||||
* group when the key is in wp_core).
|
|
||||||
*
|
|
||||||
* @var array<string,string>
|
|
||||||
*/
|
|
||||||
private const POST_TYPE_GROUP_MAP = array(
|
|
||||||
'product' => 'wc_product',
|
|
||||||
'hp_listing' => 'hp_listing_core',
|
|
||||||
'hp_request' => 'hp_request_core',
|
|
||||||
'hp_vendor' => 'hp_vendor_core',
|
|
||||||
'attachment' => 'attachment',
|
|
||||||
'nav_menu_item' => 'nav_menu_item',
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register all WP_Query hooks.
|
* Register all WP_Query hooks.
|
||||||
*
|
*
|
||||||
@@ -117,8 +100,7 @@ class TMDO_Post_Query_Router {
|
|||||||
// Confirm the field's group is appropriate for this post_type:
|
// Confirm the field's group is appropriate for this post_type:
|
||||||
// either cross-cutting wp_core (any) or the post_type-specific group.
|
// either cross-cutting wp_core (any) or the post_type-specific group.
|
||||||
$group = $field['group'] ?? '';
|
$group = $field['group'] ?? '';
|
||||||
$expected_grp = self::POST_TYPE_GROUP_MAP[ $pt ] ?? null;
|
$is_appropriate = ( 'wp_core' === $group ) || in_array( $group, TMDO_Post_Fields::groups_for_post_type( (string) $pt ), true );
|
||||||
$is_appropriate = ( 'wp_core' === $group ) || ( null !== $expected_grp && $expected_grp === $group );
|
|
||||||
if ( ! $is_appropriate ) {
|
if ( ! $is_appropriate ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core routing code resolves group ↔ post_type / table from the registry.
|
||||||
|
*
|
||||||
|
* HivePress groups are registered by the HivePress AddOn with their post
|
||||||
|
* types; core must not hardcode their names. The HivePress-only legacy
|
||||||
|
* cutover tooling (wpdo_hot_hp_listing → hp_listing_core) and the stress
|
||||||
|
* testers are out of scope and not checked here.
|
||||||
|
*/
|
||||||
|
class NoHpMappingsTest extends TestCase {
|
||||||
|
|
||||||
|
private const FILES = array(
|
||||||
|
'includes/class-tmdo-post-shadow-verifier.php',
|
||||||
|
'includes/migration/class-tmdo-post-migration.php',
|
||||||
|
'includes/query/class-tmdo-post-query-router.php',
|
||||||
|
'includes/class-tmdo-term-comment-backfill.php',
|
||||||
|
'includes/class-tmdo-term-comment-shadow-verifier.php',
|
||||||
|
'cli/class-tmdo-cli-term-comment.php',
|
||||||
|
'includes/class-tmdo-installer.php',
|
||||||
|
'includes/class-tmdo-rest-api.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Stress-test tooling inside the REST API class keeps its sample post types. */
|
||||||
|
private const ALLOWED = array(
|
||||||
|
'includes/class-tmdo-rest-api.php' => array( "array( 'product', 'hp_listing', 'hp_request', 'hp_vendor', 'attachment', 'nav_menu_item', 'post' )" ),
|
||||||
|
);
|
||||||
|
|
||||||
|
public function test_routing_code_has_no_hivepress_names(): void {
|
||||||
|
$root = dirname( __DIR__, 2 );
|
||||||
|
$offences = array();
|
||||||
|
foreach ( self::FILES as $rel ) {
|
||||||
|
$code = '';
|
||||||
|
foreach ( token_get_all( (string) file_get_contents( $root . '/' . $rel ) ) as $token ) {
|
||||||
|
if ( is_array( $token ) && in_array( $token[0], array( T_COMMENT, T_DOC_COMMENT ), true ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$code .= is_array( $token ) ? $token[1] : $token;
|
||||||
|
}
|
||||||
|
$code = str_replace( self::ALLOWED[ $rel ] ?? array(), '', $code );
|
||||||
|
if ( preg_match_all( '/hp_(listing|request|vendor|taxonomy|review|user)\w*/', $code, $m ) ) {
|
||||||
|
$offences[] = $rel . ': ' . implode( ', ', array_unique( $m[0] ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSame( array(), $offences, "Hardcoded HivePress names in core routing:\n" . implode( "\n", $offences ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_term_comment_groups_come_from_the_registry(): void {
|
||||||
|
TMDO_Entity_Registry::init();
|
||||||
|
TMDO_Entity_Registry::register_adapter( 'term', $this->createMock( TMDO_Entity_Adapter_Interface::class ) );
|
||||||
|
TMDO_Entity_Registry::register_adapter( 'comment', $this->createMock( TMDO_Entity_Adapter_Interface::class ) );
|
||||||
|
TMDO_Entity_Registry::register_group( 'term', 'x_tax', array( array( 'key' => 'x_order', 'type' => 'integer' ) ) );
|
||||||
|
TMDO_Entity_Registry::register_group( 'comment', 'x_review', array( array( 'key' => 'x_rating', 'type' => 'integer' ) ) );
|
||||||
|
|
||||||
|
$this->assertSame( array( 'x_tax' => 'term', 'x_review' => 'comment' ), TMDO_Term_Comment_Backfill::registered_groups() );
|
||||||
|
|
||||||
|
$result = TMDO_Term_Comment_Backfill::backfill_group( 'term', 'hp_taxonomy', true );
|
||||||
|
$this->assertSame( 'Unknown group: hp_taxonomy', $result['error'] ?? '', 'unregistered groups are unknown' );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -135,6 +135,24 @@ class PostFieldsRegistrationTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── group ↔ post_type mapping ────────────────────────────────────────────
|
||||||
|
|
||||||
|
public function test_core_groups_map_to_their_post_types(): void {
|
||||||
|
$this->assertSame( 'attachment', WPDO_Post_Fields::post_type_for_group( 'attachment' ) );
|
||||||
|
$this->assertSame( 'product', WPDO_Post_Fields::post_type_for_group( 'wc_product' ) );
|
||||||
|
$this->assertSame( 'nav_menu_item', WPDO_Post_Fields::post_type_for_group( 'nav_menu_item' ) );
|
||||||
|
$this->assertNull( WPDO_Post_Fields::post_type_for_group( 'wp_core' ), 'wp_core spans every post type' );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_scoped_groups_map_through_the_registry(): void {
|
||||||
|
WPDO_Entity_Registry::register_group( 'post', 'x_core', array( array( 'key' => 'x_price', 'type' => 'decimal' ) ), array( 'post_types' => array( 'x_type' ) ) );
|
||||||
|
|
||||||
|
$this->assertSame( 'x_type', WPDO_Post_Fields::post_type_for_group( 'x_core' ) );
|
||||||
|
$this->assertSame( array( 'x_core' ), WPDO_Post_Fields::groups_for_post_type( 'x_type' ) );
|
||||||
|
$this->assertSame( array( 'wc_product' ), WPDO_Post_Fields::groups_for_post_type( 'product' ) );
|
||||||
|
$this->assertNull( WPDO_Post_Fields::post_type_for_group( 'hp_listing_core' ), 'unregistered HivePress group is unknown to core' );
|
||||||
|
}
|
||||||
|
|
||||||
// ── nav_menu_item group ──────────────────────────────────────────────────
|
// ── nav_menu_item group ──────────────────────────────────────────────────
|
||||||
|
|
||||||
public function test_nav_menu_item_has_8_keys(): void {
|
public function test_nav_menu_item_has_8_keys(): void {
|
||||||
|
|||||||
@@ -155,6 +155,17 @@ class RestApiTest extends TestCase {
|
|||||||
$this->assertIsArray( $response->get_data() );
|
$this->assertIsArray( $response->get_data() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_default_post_type_is_post_and_filterable(): void {
|
||||||
|
$this->assertSame( 'post', WPDO_REST_API::default_post_type() );
|
||||||
|
|
||||||
|
$GLOBALS['_wp_filter_callbacks']['tmdo_rest_default_post_type'][] = static fn() => 'hp_listing';
|
||||||
|
try {
|
||||||
|
$this->assertSame( 'hp_listing', WPDO_REST_API::default_post_type() );
|
||||||
|
} finally {
|
||||||
|
unset( $GLOBALS['_wp_filter_callbacks']['tmdo_rest_default_post_type'] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function test_get_listings_rejects_unregistered_post_type(): void {
|
public function test_get_listings_rejects_unregistered_post_type(): void {
|
||||||
$req = new WP_REST_Request( 'GET', '/wpdo/v1/listings' );
|
$req = new WP_REST_Request( 'GET', '/wpdo/v1/listings' );
|
||||||
$req->set_param( 'post_type', 'no_such_type' );
|
$req->set_param( 'post_type', 'no_such_type' );
|
||||||
|
|||||||
Reference in New Issue
Block a user