Как изменить SQL запрос для функции get_pages()

Допустим нам необходимо изменить параметры запроса функции get_pages() так, чтобы на странице админки «Настройки чтения» изменились пункты выпадающего списка.

Нам нужно показать только страницы первого уровня.

Проблема тут в том что для функции wp_dropdown_pages() и функции get_pages() на основе которой она работает, нет ни одного фильтра, чтобы изменить параметры запроса. Справедливо для WP 5.7.

Единственный вариант изменить запрос, это хук query, который позволяет изменить запрос, непосредственно перед самим запросом.

add_filter( 'query', 'get_pages__change_sql_for_admin_pane' );

/**
 * Change SQL query for get_pages() function.
 *
 * @param string $query
 *
 * @return string mixed
 */
function get_pages__change_sql_for_admin_pane( $query ){

	if(
		/* start render admin page HTML */
		! ( did_action( 'in_admin_header' ) || did_action( 'customize_controls_head' ) )
		/* get_pages() function only */
		|| ! in_array( 'get_pages', wp_list_pluck( debug_backtrace(), 'function' ), 1 )
		//|| ! in_array( get_current_screen()->id, [ 'options-reading', 'options-privacy', 'edit-page', 'page', 'customize' ], 1 )
	)
		return $query;

	// the query to change:
	// SELECT * FROM wp_posts WHERE (post_type = 'page' AND post_status IN ('draft', 'publish')) ORDER BY wp_posts.post_title ASC
	// SELECT * FROM wp_posts  WHERE (post_type = 'page' AND post_status = 'publish')     ORDER BY wp_posts.post_title ASC
	$split = preg_split( '/(WHERE|ORDER BY)/', $query, -1, PREG_SPLIT_DELIM_CAPTURE );
	$WHERE = $split[2];

	// do our replace!
	if(
		/* for 'page' post type only */
		strpos( $WHERE, "post_type = 'page'" )
		/* check what we want to add in the query */
		&& ! strpos( $WHERE, 'post_parent' )
	){

		$grip = "post_type = 'page'";
		$add = 'AND post_parent = 0';

		$query = str_replace( $grip, "$grip $add", $query );
	}

	return $query;
}

В результате получим:

ВАЖНО! get_pages() кэширует результат в объектный кэш, поэтому если у вас установлен плагин объектного кэширования, который сохраняет результаты между генерациями страницы, хук может не работать или он может влиять на результат такого-же запроса на какой-либо другой странице!

Удаление страниц с шаблоном из выпадающего списка страниц при выборе родителя

Создадим файл remove-companies-from-get_peges.php и подключим его в function.php.

<?php

// удаление страниц с шаблоном 'page_compani*' пр: 'page_compani.php' из выпадающего списка страниц при выборе родителя

if( ! is_admin() ){
	return;
}

add_filter( 'query', 'get_pages__change_sql_for_admin_pane' );

/**
 * Change SQL query for get_pages() function.
 *
 * @param string $query
 *
 * @return string mixed
 */
function get_pages__change_sql_for_admin_pane( $query ){

	if(
		! did_action( 'current_screen' )
		|| ( 'post-page' !== get_current_screen()->base .'-'. get_current_screen()->id )
		|| ! in_array( 'get_pages', wp_list_pluck( debug_backtrace(), 'function' ), 1 )
	){
		return $query;
	}

	list( $SELECT, $WHERE, $ORDER_BY ) = preg_split( '/(WHERE|ORDER BY)/', $query, -1 );

	// change sql
	if(
		/* for 'page' post type only */
		strpos( $WHERE, "post_type = 'page'" )
	){
		global $wpdb;

		$page_tpl_patt = 'page_compani%';

		// note: $wpdb->prepare() not work here
		$JOIN = "LEFT JOIN $wpdb->postmeta ON ( ID = post_id AND meta_key = '_wp_page_template' )";
		$new_WHERE = "WHERE $WHERE AND ( meta_value NOT LIKE '$page_tpl_patt' OR meta_value IS NULL )";

		$query = "$SELECT $JOIN $new_WHERE ORDER BY $ORDER_BY";
	}

	return $query;
}