Взаимодействие с базой данных в WordPress. Класс $wpdb.

Помню, когда я только начинал использовать WordPress, я подключался к базе данных напрямую, то есть коннектился через mysql_connect() и всё такое, ну и нубство :)

Итак, приступим.

Перед тем, как использовать любой из перечисленных ниже методов, необходимо глобализовать $wpdb.

global $wpdb;

Произвольный SQL-запрос

$wpdb->query('query');

Проще простого, ведь так? А теперь Пример 1:

$wpdb->query(
	"
	DELETE FROM $wpdb->posts 
	WHERE post_type = 'revision'
	"
);
// удаляем все ревизии постов

Пример 2.
используйте $wpdb->prepare, чтобы защититься от SQL-инъекций:

$id = 15; $key = "first_name"; $value = "Михаил";
 
$wpdb->query( $wpdb->prepare( 
	"
		INSERT INTO $wpdb->usermeta
		( user_id, meta_key, meta_value )
		VALUES ( %d, %s, %s )
	", 
	$id, 
	$key, 
	$value 
) );
// %s значит строка, %d - значит число
// добавляем имя пользователю с ID=15

Пример 3.
также в качестве второго параметра можно использовать и массив:

$wpdb->query( $wpdb->prepare( 
	"
		INSERT INTO $wpdb->usermeta
		( user_id, meta_key, meta_value )
		VALUES ( %d, %s, %s )
	",
	array(
		$id, 
		$key, 
		$value 
	)
) );

Обращение к таблицам MySQL, префикс таблиц

Обратиться к стандартным таблицам в базе WordPress очень легко — ведь для этого предназначены переменные класса:

  • $commentmeta
  • $comments
  • $links
  • $options
  • $postmeta
  • $posts
  • $terms
  • $term_relationships
  • $term_taxonomy
  • $usermeta
  • $users

Как это будет выглядеть на деле:

$wpdb->query("DELETE FROM $wpdb->posts WHERE id = '1'");

Ок, а как быть с таблицами плагинов, или теми, которые сами мы и создали?

$wpdb->query("DELETE FROM ".$wpdb->prefix."posts WHERE id='1'")

Как использовать SELECT?

На этот случай в WordPress существует аж четыре метода (функции). По-прежнему не хочу загружать вас лишней болтовней, поэтому сразу перехожу к делу.

1. get_results

Первый метод больше всего похож на обычный запрос SELECT.

$wpdb->get_results('query', output_type);
  • output_type — тип вывода данных
    • OBJECT — объект (по умолчанию),
    • OBJECT_K — ассоциативный массив, в котором в качестве ключей будут использоваться значения в первой колонке, дубликаты будут проигнорированы,
    • ARRAY_A — нумерованный массив состоящий из ассоциативных массивов, в качестве индексов которых будут использоваться названия колонок,
    • ARRAY_N — нумерованный двумерный массив;

Пример 1.
выводим заголовки всех опубликованных страниц:

$pages = $wpdb->get_results( 
	"
	SELECT post_title, post_content 
	FROM $wpdb->posts
	WHERE post_status = 'publish' 
	AND post_type = 'page'
	"
);
/* вытаскивает из базы данных заголовки и содержимое
всех опубликованных страниц */
if( $pages ) {
	foreach ( $pages as $page ) {
		echo $page->post_title;
	}
}
// выводим заголовки

Пример 2.
пример с использованием функции setup_postdata, то есть в итоге получается некий аналог WP_Query().

$pages = $wpdb->get_results( 
	"
	SELECT * 
	FROM $wpdb->posts
	WHERE post_status = 'publish' 
	AND post_type = 'page'
	"
);
if ( $pages ) {
	foreach ( $pages as $post ) {
		setup_postdata( $post );
		?>
		<h1>
			<a href="<?php the_permalink(); ?>">
				<?php the_title(); ?>
			</a>
		</h1>
		<?php
	}
	else
	{
		?>
		<h1>Ничего не найдено</h1>
		<?php
	}
}
/* В случае если на блоге есть опубликованные страницы,
выводим заголовок со ссылкой на саму страницу */

2. get_var

Переходим ко второму методу, get_var, в отличие от get_results, он позволяет вывести одно значение, причем это может быть сумма значений или количество строк.

$wpdb->get_var('query',column_offset,row_offset);
  • column_offset — (целое) номер по порядку нужной колонки (по умолчанию 0)
  • row_offset — (целое) номер по порядку нужного ряда (по умолчанию 0)

Пример первый.
выводим дату регистрации какого-то конкретного пользователя:

$date = $wpdb->get_var(
	"
	SELECT user_registered
	FROM $wpdb->users
	WHERE user_login = 'truemisha'
	"
);
echo $date;
/* узнаем и выводим дату регистрации пользователя
с логином truemisha */

Пример второй.
пришло время узнать количество зарегистрированных пользователей на нашем блоге:

$number_of_users = $wpdb->get_var(
		"SELECT COUNT(*) FROM $wpdb->users;"
);
echo "Всего на сайте зарегалось {$number_of_users} человек.";

Пример третий
подсчитаем сумму чего-нибудь из метаданных пользователя:

$amount = $wpdb->get_var( $wpdb->prepare( 
	"
		SELECT sum(meta_value) 
		FROM $wpdb->usermeta 
		WHERE meta_key = %s
	", 
	$meta_key
) );
echo "Сумма равна {$amount}";

3. get_row

Получает значения только из одной строки, удовлетворяющей условию.

$wpdb->get_row('query', output_type, row_offset);
  • output_type — тип вывода данных,
    • OBJECT — объект (по умолчанию),
    • ARRAY_A — ассоциативный массив,
    • ARRAY_N — нумерованный массив;
  • row_offset — (целое) номер по порядку нужного ряда (по умолчанию 0)

Пример 1.
выводим заголовок поста с самым большим количеством комментариев:

$post = $wpdb->get_row(
	"
	SELECT post_title, post_content
	FROM $wpdb->posts
	WHERE post_status = 'publish'
	ORDER BY comment_count DESC LIMIT 0,1
	"
);
echo $post->title;

Пример 2.
используем для тех же целей ассоциативный массив:

$post = $wpdb->get_row(
	"
	SELECT post_title, post_content
	FROM $wpdb->posts
	WHERE post_status = 'publish'
	ORDER BY comment_count DESC LIMIT 0,1
	",
	ARRAY_A
);
echo $post['title'];

4. get_col

Вывод данных только из одной колонки, в результате получаем одномерный объект.

$wpdb->get_col('query',column_offset);
  • column_offset — (целое) номер по порядку нужной колонки (по умолчанию 0)

Пример:
выводим заголовок самой первой опубликованной страницы:

$posts = $wpdb->get_col(
	"
	SELECT post_title
	FROM $wpdb->posts
	WHERE post_status = 'publish'
	AND post_type='page'
	"
);
echo $posts[0];

INSERT

Предназначен для вставки строки в таблицу. Сразу перейду к примеру, потому что в синтаксисе на первый взгляд будет мало чего понятно.

$wpdb->insert( 
	$wpdb->prefix . 'usermeta', // указываем таблицу
	array( // 'название_колонки' => 'значение'
		'user_id' => 1,
		'meta_key' => 'first_name', 
		'meta_value' => 'Михаил' 
	), 
	array( 
		'%d', // %d - значит число
		'%s', // %s - значит строка
		'%s'
	) 
);
вставка строки в MySql таблицу INSERT

UPDATE

Предположим в прошлом примере мы всё-таки создали строку и сейчас хотим её обновить:

$wpdb->update( 
	$wpdb->prefix . 'usermeta', // указываем таблицу
	array('meta_value' => 'Миша'), // поменяем имя 
	array( // где 
		'user_id' => 1,
		'meta_key' => 'first_name'
	), 
	array( '%s' ),
	array( // формат для &laquo;где&raquo;
		'%d',
		'%s'
	)
);
UPDADE - обновление строки в таблице MySql
Подпишитесь, чтобы раз в неделю получать свежие статьи с блога по email.

Смотрите также

Комментарии 35

  • volodymyrl4 апреля 2012 в 22:04 #

    спасибо, полезно

  • Человек3 июля 2012 в 09:07 #

    Спасибо за обзор - полезно

  • Александр20 ноября 2012 в 19:11 #

    Михаил, а как быть с конструкцией ?

    while ($row = mysql_fetch_assoc($result))
    {
    ...
    }
    • Миша21 ноября 2012 в 06:11 #

      а зачем она тебе?)

      • Александр21 ноября 2012 в 15:11 #

        В исходном коде (когда я напрямую подключаюсь к БД) она присутствует.
        Сейчас хочу переделать под $wpdb и, поэтому, спрашиваю, как быть?

      • Миша21 ноября 2012 в 20:11 #

        ясно) она не нужна, в вордпрессовском варианте задачу этой функции выполняет foreach

  • Александр22 ноября 2012 в 14:11 #

    Я уже выкрутился через count и for

  • Александр25 ноября 2012 в 00:11 #

    Кстати, Михаил, ты не тестировал что быстрее: коннектиться напрямую, или через wpdb ?

    • Миша25 ноября 2012 в 08:11 #

      если не используешь wp, то коннектись напрямую, если wp, то через $wpdb :)
      вряд ли сайту прибавит скорости твоё дополнительное некэшируемое подключение к базе)

  • Александр17 декабря 2012 в 19:12 #

    А в чем суть $wpdb->prepare ?
    От чего именно он фильтрует (пример из практики)?

    • Миша20 декабря 2012 в 05:12 #

      думаю это надо спросить у того, кто занимается инъекциями.
      я честно говоря хз, хотел в гугле поискать - не помогло)

  • Александр21 декабря 2012 в 19:12 #

    во-во... я напоролся на такую "инъекцию", когда в поле ввел знак процента (По тексту нужно было написать о скидке). Весь текст до него сохранился, а затем "обрыв связи".
    Так что уже один признак инъекций найден (%)

  • Salat20 декабря 2013 в 04:12 #

    Не получается обновить user_nicename хуком user_update
    var_dump($user_id) возвращает правильный id вида int(10)
    Причем не инсертится вообще ни в одну таблицу. Есть идеи в чем косяк?

    add_action('user_register','write_new_nicename',10);
    function write_new_nicename($user_id){
    global $wpdb;
    $wpdb->update(
    $wpdb->prefix . 'users',
    array('user_url' => $user_id),
    array(
    'ID' => $user_id,
    'user_login' => 'display_name'
    ),
    array( '%s' ),
    array(
    '%d',
    '%s'
    )
    );

  • Женька17 января 2015 в 10:01 #

    Блин, просто МЕГА спасибо за вот эту конструкцию:

    	foreach ( $pages as $page ) {
    		echo $page->post_title;
    	}

    Как только не пробовала выводить атрибуты из БД, и через массив и так и сяк)))) СПАСИБО!

  • Сергей22 мая 2015 в 10:05 #

    Всем привет.
    Такой вопрос: а как можно проверить наличие записи в таблице? Аяксом записываю в таблицу 'usermeta', тут же на странице без перезагрузки редактирую данные и они должны автоматом обновиться.
    Мне нужно проверить, существует ли такая запись и только потом делать Update, я правильно понимаю? Как это реализовать с помощью '$wpdb->insert' и '$wpdb->update'?
    Спасибо.

    • Миша23 мая 2015 в 08:05 #

      Привет!
      Попробуйте так:

      $sql = "SELECT EXISTS (SELECT * FROM `usermeta` WHERE id = '{$id}')";

      Если же я правильно понял и вы работаете с таблицей wp_usermeta, то в WP уже есть функции для этого, например get_user_meta().

  • Сергей22 мая 2015 в 10:05 #

    И ещё вопрос: '$wpdb->prepare' используется для фильтра...
    А почему он не используется в '$wpdb->insert' и '$wpdb->update'?
    Спасибо.

  • Николай25 мая 2015 в 19:05 #

    Хочу создать блок, типа саммое комментируемое: Название поста и иконка с количеством комментариев.
    Попробовал код

    $post = $wpdb->get_row(
    	"
    	SELECT post_title, post_content
    	FROM $wpdb->posts
    	WHERE post_status = 'publish'
    	ORDER BY comment_count DESC LIMIT 0,1
    	"
    );
    echo $post->title;

    че-то не работает, как и вариант через ассоциативный массив.
    Другие конструкции работают.
    И еще вопрос: что такое "DESC LIMIT?"

    • Миша26 мая 2015 в 08:05 #

      Одназначно нужен вариант через WP_Query:

      $posts_by_comments = new WP_Query('posts_per_page=5&orderby=comment_count');

      DESC - сортировка от большего к меньшему.
      LIMIT - ограничение количества вытаскиваемых строк.

  • Сергей11 октября 2015 в 01:10 #

    А как добавить запись в подчиненную таблицу сразу после использования $wpdb->insert ? Можно пример?

    • Миша11 октября 2015 в 08:10 #

      Либо я не понял вопроса, либо — в статье всё есть.

      • Сергей13 октября 2015 в 03:10 #

        уточню. как перед записью в подчиненную таблицу получить id вставленной записи в главную таблицу? при попытке следом, получить запросом id, прерывается выполнение и не записывается в главную таблицу.

        • Миша13 октября 2015 в 14:10 #

          получить id вставленной записи

          в данном случае необходимо делать именно mysql запрос?

          для такой цели очень удобной будет wp_insert_post().

  • Антон28 октября 2015 в 14:10 #

    Здравствуйте! Подскажите пожалуйста, как сделать поле запроса и поиск по базе определенного словосочетания с последующим выводом на экран?

  • Михаил30 ноября 2015 в 12:11 #

    Здравствуйте. Подскажите как подсчитать количество постов с условием фильтра категория и метка. Нужно именно с помощью $wpdb.

    Вот этот код выводит только по категории

    В категории 
    опубликовано <?php 
    $cat = '3462';
    echo $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts INNER JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id) INNER JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id) WHERE $wpdb->term_taxonomy.taxonomy = 'category' AND $wpdb->term_taxonomy.term_id = '$cat' AND post_type = 'post' AND post_status = 'publish'"); ?> записей

    А как добавить в условие еще метку?

    • Миша30 ноября 2015 в 13:11 #

      Здравствуйте!
      А не пробовали добавить ещё это?

      $wpdb->term_taxonomy.taxonomy = 'post_tag'
      • Михаил30 ноября 2015 в 14:11 #

        Спасибо за помощь.
        Мне нужно добавить именно по конкретной метке, или slug или id метки.

        вот тут сделано для категории
        WHERE $wpdb->term_taxonomy.taxonomy = 'category'
        AND $wpdb->term_taxonomy.term_id = '$cat' //ид категории

        как-то нужно сюда еще добавить

        WHERE $wpdb->term_taxonomy.taxonomy = 'post_tag'
        AND $wpdb->term_taxonomy.term_id = '$tagid' // ид метки

        как правильно это все совместить?

        • Миша2 декабря 2015 в 07:12 #

          Прошу прощения за задержку с ответом:

          WHERE ($wpdb->term_taxonomy.taxonomy = 'category' AND $wpdb->term_taxonomy.term_id = '$cat')
          OR ($wpdb->term_taxonomy.taxonomy = 'post_tag' AND $wpdb->term_taxonomy.term_id = '$tagid');
          • Михаил2 декабря 2015 в 13:12 #

            Спасибо.
            Подсчитывает если оставить что-то одно или post_tag или category, а по двум не хочет. И я заменил еще OR на AND. (но так или иначе не работает в обоих случаях)

            Вот полный пример

            $cat = '4218'; // id категории
            $tagid = '4115'; // id метки
            $pagescpunt = $wpdb->get_var("SELECT COUNT(*) 
            FROM $wpdb->posts 
            INNER JOIN $wpdb->term_relationships 
            ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id) 
            INNER JOIN $wpdb->term_taxonomy 
            ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id) 
             
            WHERE ($wpdb->term_taxonomy.taxonomy = 'category' 
            AND $wpdb->term_taxonomy.term_id = '$cat')
             
            AND ($wpdb->term_taxonomy.taxonomy = 'post_tag' 
            AND $wpdb->term_taxonomy.term_id = '$tagid')
             
            AND post_type = 'post' 
            AND post_status = 'publish'
             
             
            "); 
             
            print_r($pagescpunt);

            В целом нужно сделать аналогично функции

            $res_search = &new WP_Query( 'tag=domik&cat=4');
            echo $res_search->post_count;

            • Миша4 декабря 2015 в 08:12 #

              Понятно. А возможно вопрос — почему через WP_Query не получается?

              P.S. можете полазить собственно в коде класса WP_Query, это может помочь.

              • Михаил4 декабря 2015 в 10:12 #

                С помощью WP_Query получается, просто все это дело используется еще в цикле.
                Нужно посчитать количество не только одной категории, а у нескольких дочерних категорий. И все это дело генерит кучу запросов к базе(при 40 000 записях и 500 категориях, таким образом получается около 300 запросов).
                Вот пример кода.

                <?php
                 $tags_s = 'rabota'; // слаг метки
                 $news_cat_ID = '444'; // ид категори
                 $news_cats = get_categories("parent=$news_cat_ID");//для вывода дочерних категорий
                 
                 foreach ($news_cats as $news_cat):
                 ?>
                 <?php 
                $res_search = &new WP_Query( 'tag='.tags_s.'&cat='.$news_cat);
                $yyyy = $res_search->post_count; 
                if ( $yyyy  > '0' ) { чтобы не выводить ссылки где записей 0
                 
                echo '<li><a href="http://site.com/?category_name=' . $news_cat->slug. '&tag='.$tags_s.'" >' . $news_cat->name.'</a> ('.$yyyy.')</li>'; 
                 
                 } ?> 
                 
                <?php endforeach; ?>

                Вот я и подумал, что можно уменьшить кол-во запросов к базе с помощью $wpdb

  • Константин12 января 2016 в 08:01 #

    Спасибо, хорошая статья

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

phpjsHTMLCSSSQLПросто код
  Для того, чтобы оставить комментарий, пожалуйста, зарегистрируйтесь или авторизуйтесь на сайте.
Получайте новости блога по email или следите за мной в социальных сетях.
  • alex morozov: Ура, спасибо! Все-таки я разобрался ! Сделал:

  • alex morozov: Сделал Класс добаляется даже если поле пустое

  • Vladislav: Лучше бы посоветовали изящное решение имеющейся проблемы.

  • alex morozov: Не очень понял. Я добавляю метабокс через класс, почти как у вас. class Desc_Meta_Box { private $screens = array(...

  • Миша: Метод тыка хорош, но оснвоы нужно знать в любом случае. Рекомендую почитать это.