pre_user_query. Фильтрация пользователей перед выводом на сайте (изменение SQL запроса).

Прежде всего нужно понять один важный момент. Когда я только увидел в коде экшн pre_user_query, я подумал: «А, наверное это что-то наподобие pre_get_posts, но только для пользователей».

Ничего подобного. На самом деле существуют два основных фильтра, связанных с полечением пользователей:

  1. pre_get_users (как раз он и является аналогом pre_get_posts для постов) выполняется до того, как спарсится SQL запрос и позволяет поменять какие-то параметры WP_User_Query, например количество выводимых пользователей, их сортировка и т.д.
  2. pre_user_query вступает в действие уже после того, как спарсится SQL запрос, непосредственно перед его выполнением. Ему и посвятим этот пост.

Если вы пришли сюда не за готовым примером, а для того, чтобы во всём разобраться, то предлагаю начать с того, чтобы вставить код ниже в functions.php вашей текущей темы и перейти на любую страницу сайта, где присутствует вывод пользователей, например в список пользователей в админке.

add_action( 'pre_user_query', 'true_funkciya' );
 
function true_funkciya( $zapros ){
	print_r( $zapros );
}

Статья рассчитана на пользователей, которые уже кое-в-чём понимают, поэтому я не буду объяснять значение простых функций наподобие print_r().

И ещё кое-что, если ваша версия WordPress ниже, чем 3.1, то pre_user_query у вас работать не будет (попробуйте использовать вместо неё pre_user_search, а ещё лучше — обновитесь).

Окей, а теперь давайте на примерах посмотрим, что с этим можно сделать.

1. Скрытый пользователь.

Итак, как полностью скрыть какого-либо пользователя из сайта и админки, но при этом сам он себя будет видеть. Конечно при помощи pre_user_query.

add_action('pre_user_query','true_skrivaem_polzovatelya');
 
function true_skrivaem_polzovatelya( $zapros ) {
 
	// так как сам себя пользователь видеть должен, поэтому нам понадобится следующее условия
	$tekuchij_polzovatel = wp_get_current_user();
	if ($tekuchij_polzovatel->ID != 2) { // я взял пользователя с ID = 2, вы можете использовать любого другого
		global $wpdb;
		// используем обычную str_replace() для того, чтобы исключить пользователя из SQL запроса 
		$zapros->query_where = str_replace('WHERE 1=1', "WHERE 1=1 AND {$wpdb->users}.ID<>2", $zapros->query_where); // если будете менять ID, то не забудьте поменять и тут
	}
 
}

Что можно понять из этого примера?

  • То, что я извращаюсь с транслитом (надеюсь, это не слишком мешает, просто у меня есть ещё и англоязычный блог, на который я переношу многие из статей — не хочу получить дубликаты кода).
  • Условие на проверку текущего пользователя вы кстати можете вынести за пределы функции и поставить под условие add_action(), в принципе получится вот так:
    $tekuchij_polzovatel = wp_get_current_user();
    if ($tekuchij_polzovatel->ID != 2)
    	add_action('pre_user_query','true_skrivaem_polzovatelya');
  • Хоть пользователь и пропадёт из сайта, для того, чтобы полностью всех обмануть, вам также придётся поработать со счетчиками количества пользователей, для того, чтобы вам было понятно, о чём я говорю, вот скриншот:
    Несоответствие отображаемого количества пользователей и значения счетчика.

    Но не стоит беспокоиться, этот счетчик тоже можно спокойно поправить парой хуков, только это уже пожалуй тема отдельного поста.

  • Как видите, мы просто заменили WHERE-часть запроса, те, кто хорошо знаком с базами данных, увидит здесь гораздо большую свободу действий.

2. Если пользователь не администратор, то скрываем всех администраторов

По сути всё основывается на том же принципе, что и в предыдущем примере, только SQL-запрос станет намного интереснее. Пример будет также основываться на ролях пользователей, про которые можно почитать здесь.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
add_action('pre_user_query','true_skrivaem_administratorov');
 
function true_skrivaem_administratorov( $zapros ) {
 
	// так же, как и в предыдущем примере получаем текущего пользователя и убеждаемся, что он не администратор
	// разумеется тут можно использовать любую свою проверку
	$tekuchij_polzovatel = wp_get_current_user();
	if ( $tekuchij_polzovatel->roles[0] != 'administrator' ) { 
		global $wpdb;
		$zapros->query_where = str_replace(
			'WHERE 1=1', 
			"WHERE 1=1 AND {$wpdb->users}.ID IN (
				SELECT {$wpdb->usermeta}.user_id FROM $wpdb->usermeta 
					WHERE {$wpdb->usermeta}.meta_key = '{$wpdb->prefix}capabilities'
					AND {$wpdb->usermeta}.meta_value NOT LIKE '%administrator%')", 
			$zapros->query_where
		);
	}
}

Если вам всё ещё многое непонятно, могу порекомендовать почитать статью про базы данных в WordPress, про $wpdb тут.

Теперь же краткое описание того, что сделали — как известно, название роли пользователя хранится в таблице базы данных wp_usermeta (заменить wp_ на используемый у вас префикс) под ключом wp_capabilities. И в примере мы получили только тех пользователей, у которых в значении wp_capabilities в базе данных отсутствует administrator.

3. Расширенный поиск пользователей

По умолчанию WordPress позволяет производить поиск пользователей по уже определенным полям, например по логину user_login, емайлу user_email, URL user_url, отображаемому имени display_name, айди ID и по нику user_nicename. Всё, что нам разрешается, это исключить какие-либо из колонок из поиска при помощи фильтра user_search_columns.

Однако и тут pre_user_query открывает перед нами новые возможности — мы можем можем включить в поиск как любые метаданные пользователя, так и заголовки постов!

add_action('pre_user_query','true_poisk_po_meta');
 
 
function true_poisk_po_meta( $zapros ){
	// прежде всего делаем несколько проверок, для того, чтобы код применялся только при поиске пользователей
	if ( $zapros->query_vars['search'] ){
		$poisk = trim( $zapros->query_vars['search'], '*' );
		if ( $_REQUEST['s'] == $poisk ){
			global $wpdb;
 
 			// добавляем Имя first_name пользователя в поиск
			$zapros->query_from .= " JOIN {$wpdb->usermeta} fn ON fn.user_id = {$wpdb->users}.ID AND fn.meta_key = 'first_name'";
 
			// добавляем любые метаданные пользователя
			// $zapros->query_from .= " JOIN {$wpdb->usermeta} cstm ON cstm.user_id = {$wpdb->users}.ID AND cstm.meta_key = 'ВАШ ПРОИЗВОЛЬНЫЙ meta_key'";
 
 			// ну и пример с заголовками постов (я включил все посты, любого статуса и типа, но вы можете это доработать)
			$zapros->query_from .= " JOIN {$wpdb->posts} psts ON psts.post_author = {$wpdb->users}.ID";
 
 			// решаем, по каким полям в итоге производить поиск
 			$posik_po = array( 'user_login', 'user_email', 'fn.meta_value', 'psts.post_title' );
 
 			// применяем к запросу
			$zapros->query_where = 'WHERE 1=1' . $zapros->get_search_sql( $poisk, $posik_po, 'both' );
		}
	}
}
Подпишитесь, чтобы раз в неделю получать свежие статьи с блога по email.

По теме

Оставить комментарий / вопрос

phpjsHTMLCSSSQLПросто код
  Для того, чтобы оставить комментарий, пожалуйста, зарегистрируйтесь или авторизуйтесь на сайте.
Получайте новости блога по email или следите за мной в социальных сетях.
  • Миша: Да, точно)

  • Владимир: и на третьей строке не хватает скобки ) закрывающей if :)

  • Миша: Добрый вечер! Рекомендую получить ID пользователя через функцию get_current_user_id().

  • Дмитрий: Миша, подскажите пожалуйста, я использую такой редирект, но он не срабатывает: add_action( 'template_redirect', funct...

  • Миша: Ну как не определена - это аргумент функции. Или я просто вас не до конца понимаю.