Взаимодействие с базой данных в 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( // формат для «где»
		'%d',
		'%s'
	)
);
UPDADE - обновление строки в таблице MySql

Миша

В последние годы я долго не знал, что мне делать с сайтом misha.blog, ведь он практически не приносит никакого профита, но недавно я осознал, что моя миссия – способствовать распространению WordPress. Ведь WordPress – это лучший движок для разработки сайтов – как для тех, кто готов использовать заложенную структуру этой CMS, так и для тех, кто предпочитает headless решения.

Сам же я впервые познакомился с WordPress в 2009 году. Организатор WordCamp. Преподаватель в школах Epic Skills и LoftSchool.

Если вам нужна помощь с вашим сайтом или может даже разработка с нуля на WordPress / WooCommerce — пишите. Я и моя команда сделаем вам всё на лучшем уровне.

Комментарии — 42

  • volodymyrl 4 апреля 2012 #

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

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

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

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

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

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

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

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

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

      • Миша 21 ноября 2012 #

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

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

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

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

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

    • Миша 25 ноября 2012 #

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

  • Александр 17 декабря 2012 #

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

    • Миша 20 декабря 2012 #

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

  • Александр 21 декабря 2012 #

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

  • Salat 20 декабря 2013 #

    Не получается обновить 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 #

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

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

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

    • Миша 18 января 2015 #

      Пожалуйста 🙂

  • Сергей 22 мая 2015 #

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

    • Миша 23 мая 2015 #

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

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

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

  • Сергей 22 мая 2015 #

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

    • Миша 23 мая 2015 #

      Используется, прост в посту я не стал добавлять его в каждый из примеров.

  • Николай 25 мая 2015 #

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

    $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 #

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

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

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

  • Сергей 11 октября 2015 #

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

    • Миша 11 октября 2015 #

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

      • Сергей 13 октября 2015 #

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

        • Миша 13 октября 2015 #

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

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

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

  • Антон 28 октября 2015 #

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

    • Миша 29 октября 2015 #

      Здравствуйте!
      Для поиска по базе используйте: LIKE 'поиск_запрос'.

  • Михаил 30 ноября 2015 #

    Здравствуйте. Подскажите как подсчитать количество постов с условием фильтра категория и метка. Нужно именно с помощью $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 #

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

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

        Спасибо за помощь.
        Мне нужно добавить именно по конкретной метке, или 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 #

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

          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 #

            Спасибо.
            Подсчитывает если оставить что-то одно или 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 #

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

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

              • Михаил 4 декабря 2015 #

                С помощью 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 #

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

  • Виталий Моргунов 13 апреля 2017 #

    Михаил, доброго времени суток!
    Не подскажете, как лучше организовать обновление таблицы БД?
    Т.е. у меня имеются новые данные и есть таблица со старыми данными. Нужно удалить все старые строки и вставить новые.

    • Миша 14 апреля 2017 #

      Доброго!

      Сначала можно через DELETE удалить всё, что в таблице, затем использовать ALTER TABLE название_таблицы AUTO_INCREMENT = 1, чтобы сбросить инкремент, а потом вставляем новые через INSERT. Как-то так 🙂

  • Дмитрий 17 января 2020 #

    Может вы мне поможете.
    Создаю таблицу например test
    Но в запросах потом к ней не получается использовать структуру
    $wpdb->test
    хотя wp_test работает
    В чем может быть причина?

    • Миша 18 января 2020 #

      Причина, потому что такая структура не определена, юзайте $wpdb->prefix . 'text' и всё будет хорошо.

  • Руслан 18 марта 2020 #

    Понятно что ничего не понятно. Много воды, а по факту я для себя ничего не увидел

    • Миша 18 марта 2020 #

      Бывает, возвращайтесь, когда будете лучше разбираться в теме

  • Наталья 21 марта 2020 #

    А как записать данные из большого массива (скажем 100 000 строк) в базу данных? Есть ведь ограниченное количество соединений. Можно ли использовать подключение к базе, внутри цикла или как в этом случае лучше поступить? Нужно ли при этом закрывать соединение? И о использовании commit тоже хотелось бы в этом случае узнать

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

Если вы хотите добавить код, не забудьте обернуть его в <pre lang="php"></pre>, если же код – меньше одной строчки, то можно и в <code></code>.