Перед вами подробное руководство по построению запросов к базе данных WordPress при помощи объекта $wpdb
класса wpdb
.
Класс позволяет взаимодействовать с базой данных без построения SQL-запросов на чистом PHP.
При использовании объекта $wpdb для построения SQL-запросов, нам следует определить его как глобальную переменную.
Например:
global $wpdb; $results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}options WHERE option_id = 1", OBJECT );
Этот пример позволяет получить опцию из таблицы wp_options
со значением колонки option_id
равным единице. Если мы сделаем print_r( $results )
, то получим:
Array ( [0] => stdClass Object ( [option_id] => 1 [option_name] => siteurl [option_value] => https://misha.agency [autoload] => yes ) )
Любые данные, которые вы передаёте в свои SQL-запрсоы должны очищаться, иначе вы повышаете риск SQL-инъекций для вашего сайта. Метод prepare
отлично с этим справляется, он также поддерживает синтаксис sprintf() и vsprintf().
Понять принцип его работы лучше всего на примере.
$meta_key = 'seo_title'; $meta_value = 'Привет, мир!'; $wpdb->query( $wpdb->prepare( " INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value ) VALUES ( %d, %s, %s ) ", 10, $meta_key, $meta_value ) );
Давайте разберём этот пример:
$wpdb->prepare()
подготовливает SQL-запрос для $wpdb->query()
и находится непосредственно внутри него.sprintf()
, в первом параметре у нас находится SQL-запрос INSERT INTO
в виде строки, в котором переменные заменены на плейсхолдеры (%d
и %s
), которые будут заменены на переменные из следующих параметров в том же порядке, в котором они указаны. Например %d
будет заменён на 10
.%d
означает целое число, %s
означает строку, больше значений можете найти в документации функции sprintf().Синтаксис vsprintf()
отличается тем, что значения передаются в запрос не бесконечным количеством переменных, а массивом.
$meta_key = 'seo_title'; $meta_value = 'Привет, мир!'; $wpdb->query( $wpdb->prepare( " INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value ) VALUES ( %d, %s, %s ) ", array( 10, $meta_key, $meta_value, ) ) );
В обоих ситуациях нам не нужно никак очищать передаваемые данные, т.к. мы передаём их через плейсхолдеры.
Обязательно используйте $wpdb->prepare()
с $wpdb->query()
, $wpdb->get_col()
, $wpdb->get_var()
, $wpdb->get_row()
, $wpdb->get_results()
.
Не нужно использовать $wpdb->prepare()
с методами $wpdb->insert()
, $wpdb->update()
, $wpdb->replace()
, $wpdb->delete()
, так как он и так уже находится внутри них.
Для того, чтобы вам было легче разобраться, в последующих примерах я уже сразу буду добавлять $wpdb->prepare()
там, где нужно!
Обратиться к стандартным таблицам в базе WordPress очень легко — ведь для этого предназначены переменные класса:
$commentmeta
$comments
$links
$options
$postmeta
$posts
$terms
$term_relationships
$term_taxonomy
$usermeta
$users
Как это будет выглядеть на деле:
$wpdb->query( $wpdb->prepare( " DELETE FROM $wpdb->posts WHERE id = '%d' ", $post_id ) );
Ок, а как быть с таблицами плагинов, или теми, которые сами мы и создали?
$wpdb->query( $wpdb->prepare( " DELETE FROM " . $wpdb->prefix . "posts WHERE id = '%d' ", $post_id ) );
Первый метод больше всего похож на обычный запрос SELECT
.
$wpdb->get_results( 'query', output_type );
OBJECT
– объект (по умолчанию)OBJECT_K
– Ассоциативный массив, в котором в качестве ключей будут использоваться значения в первой колонке, дубликаты будут проигнорированы.ARRAY_A
– Нумерованный массив состоящий из ассоциативных массивов, в качестве индексов которых будут использоваться названия колонок.ARRAY_N
– Нумерованный двумерный массив.Выводим заголовки всех опубликованных страниц:
$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; } } // выводим заголовки
Пример с использованием функции 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 ); echo '<h2><a href="' . get_permalink() . '">' . get_the_title() . '</a></h2>'; } } else { echo '<p>Ничего не найдено</p>'; }
Переходим ко второму методу, get_var()
, в отличие от get_results()
, он позволяет вывести одно значение, причем это может быть сумма значений или количество строк.
$wpdb->get_var( 'query', column_offset, row_offset );
Выводим дату регистрации какого-то конкретного пользователя:
// узнаем и выводим дату регистрации пользователя с логином truemisha $date = $wpdb->get_var( " SELECT user_registered FROM $wpdb->users WHERE user_login = 'truemisha' " ); echo $date;
Пришло время узнать количество зарегистрированных пользователей на нашем сайте на WordPress:
$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}";
Получает значения только из одной строки, удовлетворяющей условию.
$wpdb->get_row( 'query', output_type, row_offset );
OBJECT
— объект (по умолчанию),ARRAY_A
— ассоциативный массив,ARRAY_N
— нумерованный массив;Выводим заголовок поста с самым большим количеством комментариев:
$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;
Используем для тех же целей ассоциативный массив:
$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' ];
Вывод данных только из одной колонки, в результате получаем одномерный объект.
$wpdb->get_col( 'query', column_offset );
Выводим заголовок самой первой опубликованной страницы:
$posts = $wpdb->get_col( " SELECT post_title FROM $wpdb->posts WHERE post_status = 'publish' AND post_type='page' " ); echo $posts[0];
Предназначен для вставки строки в таблицу. Сразу перейду к примеру, потому что в синтаксисе на первый взгляд будет мало чего понятно.
$wpdb->insert( $wpdb->prefix . 'usermeta', // указываем таблицу array( // 'название_колонки' => 'значение' 'user_id' => 1, 'meta_key' => 'first_name', 'meta_value' => 'Михаил' ), array( '%d', // %d - значит число '%s', // %s - значит строка '%s' ) );
Предположим в прошлом примере мы всё-таки создали строку и сейчас хотим её обновить:
$wpdb->update( $wpdb->prefix . 'usermeta', // указываем таблицу array('meta_value' => 'Миша'), // поменяем имя array( // где 'user_id' => 1, 'meta_key' => 'first_name' ), array( '%s' ), array( // формат для «где» '%d', '%s' ) );
Чтобы оставить комментарий, пожалуйста, зарегистрируйтесь или войдите.
спасибо, полезно
Спасибо за обзор - полезно
Михаил, а как быть с конструкцией ?
а зачем она тебе?)
В исходном коде (когда я напрямую подключаюсь к БД) она присутствует.
Сейчас хочу переделать под $wpdb и, поэтому, спрашиваю, как быть?
ясно) она не нужна, в вордпрессовском варианте задачу этой функции выполняет foreach
Я уже выкрутился через count и for
Кстати, Михаил, ты не тестировал что быстрее: коннектиться напрямую, или через wpdb ?
если не используешь wp, то коннектись напрямую, если wp, то через $wpdb :)
вряд ли сайту прибавит скорости твоё дополнительное некэшируемое подключение к базе)
А в чем суть $wpdb->prepare ?
От чего именно он фильтрует (пример из практики)?
думаю это надо спросить у того, кто занимается инъекциями.
я честно говоря хз, хотел в гугле поискать - не помогло)
во-во... я напоролся на такую "инъекцию", когда в поле ввел знак процента (По тексту нужно было написать о скидке). Весь текст до него сохранился, а затем "обрыв связи".
Так что уже один признак инъекций найден (%)
Не получается обновить 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'
)
);
Блин, просто МЕГА спасибо за вот эту конструкцию:
Как только не пробовала выводить атрибуты из БД, и через массив и так и сяк)))) СПАСИБО!
Пожалуйста :)
Всем привет.
Такой вопрос: а как можно проверить наличие записи в таблице? Аяксом записываю в таблицу 'usermeta', тут же на странице без перезагрузки редактирую данные и они должны автоматом обновиться.
Мне нужно проверить, существует ли такая запись и только потом делать Update, я правильно понимаю? Как это реализовать с помощью '$wpdb->insert' и '$wpdb->update'?
Спасибо.
Привет!
Попробуйте так:
Если же я правильно понял и вы работаете с таблицей wp_usermeta, то в WP уже есть функции для этого, например get_user_meta().
И ещё вопрос: '$wpdb->prepare' используется для фильтра...
А почему он не используется в '$wpdb->insert' и '$wpdb->update'?
Спасибо.
Используется, прост в посту я не стал добавлять его в каждый из примеров.
Хочу создать блок, типа саммое комментируемое: Название поста и иконка с количеством комментариев.
Попробовал код
че-то не работает, как и вариант через ассоциативный массив.
Другие конструкции работают.
И еще вопрос: что такое "DESC LIMIT?"
Одназначно нужен вариант через WP_Query:
DESC - сортировка от большего к меньшему.
LIMIT - ограничение количества вытаскиваемых строк.
А как добавить запись в подчиненную таблицу сразу после использования $wpdb->insert ? Можно пример?
Либо я не понял вопроса, либо — в статье всё есть.
уточню. как перед записью в подчиненную таблицу получить id вставленной записи в главную таблицу? при попытке следом, получить запросом id, прерывается выполнение и не записывается в главную таблицу.
в данном случае необходимо делать именно mysql запрос?
для такой цели очень удобной будет wp_insert_post().
Здравствуйте! Подскажите пожалуйста, как сделать поле запроса и поиск по базе определенного словосочетания с последующим выводом на экран?
Здравствуйте!
Для поиска по базе используйте:
LIKE 'поиск_запрос'
.Здравствуйте. Подскажите как подсчитать количество постов с условием фильтра категория и метка. Нужно именно с помощью $wpdb.
Вот этот код выводит только по категории
А как добавить в условие еще метку?
Здравствуйте!
А не пробовали добавить ещё это?
Спасибо за помощь.
Мне нужно добавить именно по конкретной метке, или 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' // ид метки
как правильно это все совместить?
Прошу прощения за задержку с ответом:
Спасибо.
Подсчитывает если оставить что-то одно или post_tag или category, а по двум не хочет. И я заменил еще OR на AND. (но так или иначе не работает в обоих случаях)
Вот полный пример
В целом нужно сделать аналогично функции
$res_search = &new WP_Query( 'tag=domik&cat=4');
echo $res_search->post_count;
Понятно. А возможно вопрос — почему через WP_Query не получается?
P.S. можете полазить собственно в коде класса WP_Query, это может помочь.
С помощью WP_Query получается, просто все это дело используется еще в цикле.
Нужно посчитать количество не только одной категории, а у нескольких дочерних категорий. И все это дело генерит кучу запросов к базе(при 40 000 записях и 500 категориях, таким образом получается около 300 запросов).
Вот пример кода.
Вот я и подумал, что можно уменьшить кол-во запросов к базе с помощью $wpdb
Спасибо, хорошая статья
Михаил, доброго времени суток!
Не подскажете, как лучше организовать обновление таблицы БД?
Т.е. у меня имеются новые данные и есть таблица со старыми данными. Нужно удалить все старые строки и вставить новые.
Доброго!
Сначала можно через
DELETE
удалить всё, что в таблице, затем использоватьALTER TABLE название_таблицы AUTO_INCREMENT = 1
, чтобы сбросить инкремент, а потом вставляем новые черезINSERT
. Как-то так :)Может вы мне поможете.
Создаю таблицу например test
Но в запросах потом к ней не получается использовать структуру
$wpdb->test
хотя wp_test работает
В чем может быть причина?
Причина, потому что такая структура не определена, юзайте
$wpdb->prefix . 'text'
и всё будет хорошо.Понятно что ничего не понятно. Много воды, а по факту я для себя ничего не увидел
Бывает, возвращайтесь, когда будете лучше разбираться в теме
А как записать данные из большого массива (скажем 100 000 строк) в базу данных? Есть ведь ограниченное количество соединений. Можно ли использовать подключение к базе, внутри цикла или как в этом случае лучше поступить? Нужно ли при этом закрывать соединение? И о использовании commit тоже хотелось бы в этом случае узнать