comments_clauses │ хук-фильтр │ WC 3.1.0

Это хук WordPress - comments_clauses. Плагин его просто использует.

Позволяет изменить SQL-подзапросы (fields, join, where, orderby, limits, groupby) при выборке комментариев функцией get_comments().

Использование

add_filter( 'comments_clauses', 'wp_kama_comments_clauses_filter' );

/**
 * Function for `comments_clauses` filter-hook.
 * 
 * @param string[] $clauses Associative array of the clauses for the query.
 *
 * @return string[]
 */
function wp_kama_comments_clauses_filter( $clauses ){
	// filter...
	return $clauses;
}
$pieces(string[])
Ассоциативный массив с SQL-подзапросами типа fields, join, where, orderby, limits, groupby и так далее.
$query(WP_Comment_Query)
Текущий объект класса WP_Comment_Query. Классы в PHP передаются по ссылке.

Примеры

#1 Пример данных в фильтре

В консоли админки в метабоксе "На виду" срабатывает функция wp_dashboard_site_activity(), которая в свою очередь использует функцию wp_dashboard_recent_comments() и в последующем get_comments() для вывода последних комментариев в метабоксе. Под администратором код запроса выглядит следующим образом:

$comments_query = array(
	'number' => 25,
	'offset' => 0,
);

get_comments( $comments_query )

Посмотрим, какие данные проходят через фильтр с помощью отладки WordPress:

add_filter( 'comments_clauses', function ( $args ) {
	error_log( print_r( $args, true ) );

	return $args;
} );

Получим

Array
(
	[fields] => wp_comments.comment_ID
	[join] =>
	[where] => ( ( comment_approved = '0' OR comment_approved = '1' ) )
	[orderby] => wp_comments.comment_date_gmt DESC
	[limits] => LIMIT 0,25
	[groupby] =>
)

#2 wpDiscuz: сортировка по популярным комментариям на основе данных WP Recall

В данном кейсе используются плагины wpDiscuz и WP Recall, которые дают возможность оценивать комментарии (лайкать). Так как комментарии выводит wpDiscuz, то и сортировку он делает на основе своих данных, хранящихся в метаполях под ключом wpdiscuz_votes. На сайте используется личный кабинет на основе WP Recall и оценивая комментарии через него, пользователь получает карму, на основе которой можно делать всякие плюшки.

Задача: оставить лайки от WP Recall, убрать лайки от wpDiscuz, но оставить возможность сортировать комментарии по популярности.

Данную задачу можно решить двумя способами

Дублирование данных

Можно копировать данных из таблицы {$wpdb->prefix}rcl_rating_totals плагина WP Recall в метаданные для wpDiscuz. Это можно делать по крону или на событии оценки комментария. Рабочий способ, но так мы способствуем росту базы данных, да и выборки по метаполям как известно не отличаются быстродействием.

Подмена запроса

Что если выборку по метаполям заменить на выборку по таблице WP Recall? Плагин wpDiscuz скуп на собственные фильтры, вмешаться тут не выйдет, но благодаря системному фильтру comments_clauses мы можем это сделать!

Посмотрим какие в оригинале данные проходят через фильтр:

add_filter( 'comments_clauses', function ( $args ) {
	error_log( print_r( $args, true ) );

	return $args;
} );

// Получим следующее (оставил только то, что относится к wpDiscuz)
Array
(
	[fields] => kw_comments.comment_ID
	[join] =>  LEFT JOIN kw_commentmeta AS `cm` ON kw_comments.comment_ID = `cm`.comment_id  AND (`cm`.meta_key = 'wpdiscuz_votes')
	[where] => ( ( comment_approved = '0' OR comment_approved = '1' ) ) AND comment_post_ID = 93644 AND comment_type NOT IN ('wpdiscuz_sticky') AND comment_parent = 0
	[orderby] =>  IFNULL(`cm`.meta_value,0)+0 DESC, kw_comments.`comment_ID` asc
	[limits] => LIMIT 0,51
	[groupby] =>
	[caller] => wpdiscuz-
)

Array
(
	[fields] => kw_comments.comment_ID
	[join] =>  LEFT JOIN kw_commentmeta AS `cm` ON kw_comments.comment_ID = `cm`.comment_id  AND (`cm`.meta_key = 'wpdiscuz_votes')
	[where] => ( ( comment_approved = '0' OR comment_approved = '1' ) ) AND comment_post_ID = 93644 AND comment_type IN ('wpdiscuz_sticky') AND comment_parent = 0
	[orderby] =>  IFNULL(`cm`.meta_value,0)+0 DESC, kw_comments.`comment_ID` asc
	[limits] =>
	[groupby] =>
	[caller] => wpdiscuz-
)

Увидя паттерн, можно изменить запрос на следующий:

add_filter( 'comments_clauses', function ( $args ) {
	global $wpdb;

	$is_wpdiscuz = isset( $args['caller'] ) && $args['caller'] === 'wpdiscuz-';

	if ( $is_wpdiscuz && strpos( $args['join'], 'wpdiscuz_votes' ) !== false ) {
		$args['join']    = "LEFT JOIN {$wpdb->prefix}rcl_rating_totals AS `cm` ON $wpdb->comments.comment_ID = `cm`.object_id";
		$args['orderby'] = str_replace( 'meta_value', 'rating_total', $args['orderby'] );
	}

	return $args;
}, 11 );

// Получим
Array
(
	[fields] => kw_comments.comment_ID
	[join] => LEFT JOIN kw_rcl_rating_totals AS `cm` ON kw_comments.comment_ID = `cm`.object_id
	[where] => ( ( comment_approved = '0' OR comment_approved = '1' ) ) AND comment_post_ID = 93644 AND comment_type NOT IN ('wpdiscuz_sticky') AND comment_parent = 0
	[orderby] =>  IFNULL(`cm`.rating_total,0)+0 DESC, kw_comments.`comment_ID` asc
	[limits] => LIMIT 0,51
	[groupby] =>
	[caller] => wpdiscuz-
)

Array
(
	[fields] => kw_comments.comment_ID
	[join] => LEFT JOIN kw_rcl_rating_totals AS `cm` ON kw_comments.comment_ID = `cm`.object_id
	[where] => ( ( comment_approved = '0' OR comment_approved = '1' ) ) AND comment_post_ID = 93644 AND comment_type IN ('wpdiscuz_sticky') AND comment_parent = 0
	[orderby] =>  IFNULL(`cm`.rating_total,0)+0 DESC, kw_comments.`comment_ID` asc
	[limits] =>
	[groupby] =>
	[caller] => wpdiscuz-
)

Заметьте, я использовал в коде несколько уточнений, чтобы он применился только в нужном месте, так как фильтр comments_clauses является системным и срабатывает всегда при вызове функции get_comments().


PS: С недавних пор запрос был изменён, возможно единичный случай.

// Изменяет запрос сортировки wpDiscuz -> WP Recall по популярным комментариям
add_filter( 'comments_clauses', function ( $args ) {
	global $wpdb;

	if ( isset( $args['caller'] ) && $args['caller'] === 'wpdiscuz-' && ! is_admin() && is_singular( 'post' ) ) {
		if ( strpos( $args['where'], "comment_post_ID = " . get_the_ID() ) !== false ) {
			$args['join']    = "LEFT JOIN {$wpdb->prefix}rcl_rating_totals AS `cm` ON $wpdb->comments.comment_ID = `cm`.object_id";
			$args['orderby'] = "IFNULL(`cm`.rating_total,0)+0 DESC, $wpdb->comments.comment_ID asc";
		}
	}

	return $args;
}, 11 );

Где вызывается хук

WC_REST_Report_Reviews_Totals_Controller::get_reports()
comments_clauses
woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-report-reviews-totals-controller.php 96
$clauses = apply_filters_ref_array( 'comments_clauses', array( $clauses, &$comment_query ) );

Где используется хук в WooCommerce

woocommerce/includes/class-wc-comments.php 48
add_filter( 'comments_clauses', array( __CLASS__, 'exclude_order_comments' ) );
woocommerce/includes/class-wc-comments.php 53
add_filter( 'comments_clauses', array( __CLASS__, 'exclude_webhook_comments' ), 10, 1 );
woocommerce/includes/class-wc-comments.php 57
add_filter( 'comments_clauses', array( __CLASS__, 'exclude_action_log_comments' ), 10, 2 );
woocommerce/includes/class-wc-comments.php 61
add_filter( 'comments_clauses', array( ReviewsUtil::class, 'comments_clauses_without_product_reviews' ), 10, 2 );
woocommerce/includes/class-wc-order.php 2177
remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ) );
woocommerce/includes/class-wc-order.php 2189
add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ) );
woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-order-notes-v1-controller.php 196
remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
woocommerce/includes/rest-api/Controllers/Version1/class-wc-rest-order-notes-v1-controller.php 200
add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-order-notes-v2-controller.php 66
remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-order-notes-v2-controller.php 70
add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
woocommerce/includes/wc-order-functions.php 1283
remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
woocommerce/includes/wc-order-functions.php 1287
add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
woocommerce/packages/action-scheduler/classes/data-stores/ActionScheduler_wpCommentLogger.php 129
add_filter( 'comments_clauses', array( $this, 'filter_comment_query_clauses' ), 10, 2 );
woocommerce/src/Internal/RestApi/Routes/V4/OrderNotes/CollectionQuery.php 85
remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
woocommerce/src/Internal/RestApi/Routes/V4/OrderNotes/CollectionQuery.php 87
add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
woocommerce/src/StoreApi/Routes/V1/ProductReviews.php 127
add_filter( 'comments_clauses', $exclude_password_protected_reviews );
woocommerce/src/StoreApi/Routes/V1/ProductReviews.php 146
remove_filter( 'comments_clauses', $exclude_password_protected_reviews );