Статьи

Работа с базами данных в WordPress

Из коробки WordPress предоставляет множество функций, которые можно использовать для взаимодействия с базой данных. В большинстве случаев для выполнения работы будет WP_Query класса WP_Query и связанных с ним функций, таких как wp_insert_post , update_post_meta , get_posts . Тем не менее, иногда мы должны делать что-то, что не предусмотрено WordPress изначально, особенно когда нам нужно иметь дело с пользовательскими таблицами.

WordPress-база

В этом руководстве мы пройдемся по самому важному классу для работы с базами данных в WordPress — wpdb , включая несколько советов и приемов, которые позже могут быть реализованы в нашем wpdb разработки. Мы также коснемся dbDelta который можно использовать для создания пользовательских таблиц в нашем плагине. Мы не будем рассказывать об основах создания вашей исходной базы данных WordPress в этом руководстве, но не стесняйтесь проверить это руководство по созданию баз данных из cPanel

Работа с классом wpdb

wpdb , пожалуй, самый важный класс, который мы используем, когда нам нужно напрямую работать с базой данных. Он основан на классе ezSQL написанном Джастином Винсентом, адаптированным для работы с WordPress.

Основные методы и свойства класса wpdb уже хорошо wpdb на странице Кодекса WordPress, поэтому нет смысла повторять их здесь. Вместо этого мы рассмотрим несколько распространенных ошибок, которые могут допустить разработчики WordPress, как их исправить, а также рекомендации, которые могут быть применены при использовании класса wpdb .

Не указывайте имена таблиц в запросе SQL

Некоторые разработчики делают общее предположение, что префикс таблицы будет неизменным, и используют значение по умолчанию wp_ . Базовый пример неправильного способа сделать это показан во фрагменте ниже:

 global $wpdb ; $result = $wpdb ->get_results( 'SELECT * FROM wp_posts LIMIT 10' ); 

Это, конечно, чрезмерное упрощение того, что плагин будет на самом деле делать, но этот пример показывает, как быстро все может пойти не так. Что если наши пользователи изменят префикс таблицы на что-то другое? Это можно легко исправить, заменив строку wp_ фактическими свойствами, предоставленными с помощью prefix .

Приведенный выше код можно сделать переносимым, применив изменения, как показано ниже:

 global $wpdb ; $result = $wpdb ->get_results( 'SELECT * FROM ' . $wpdb ->prefix . 'posts LIMIT 10' ); 

Более того, если вы работаете с таблицами WordPress по умолчанию, вы можете пропустить часть префикса и вместо этого напрямую обратиться к ним как к свойствам в wpdb . Каждая таблица WordPress по умолчанию представлена ​​пользовательским свойством в классе wpdb с тем же именем таблицы без префикса.

Например, предполагая, что префикс таблицы — wp_ :

  • $wpdb->posts будут соответствовать таблице wp_posts
  • $wpdb->postmeta будет соответствовать таблице wp_postmeta
  • $wpdb->users будут соответствовать таблице wp_users

И так далее. Приведенный выше код может быть улучшен в дальнейшем, поскольку мы запрашиваем таблицу публикаций, выполняя следующие действия:

 global $wpdb ; $result = $wpdb ->get_results( 'SELECT * FROM ' . $wpdb ->posts . ' LIMIT 10' ); 

Используйте специальные вспомогательные методы для операций с базами данных

Хотя метод query предназначен для обработки любых запросов SQL, предпочтительно использовать более подходящие вспомогательные методы. Обычно это обеспечивается такими методами, как insert , update , get_row и другими. Помимо того, что он более специфичен для наших сценариев использования, он также более безопасен, поскольку о побеге и другой тяжелой работе заботятся.

Давайте посмотрим на этот пример:

 $global wpdb; $post_id = $_POST [ 'post_id' ]; $meta_key = $_POST [ 'meta_key' ]; $meta_value = $_POST [ 'meta_value' ]; $wpdb ->query( "INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value ) VALUES ( $post_id, $meta_key, $meta_value )" ); 

Помимо небезопасной природы этого фрагмента, он должен нормально работать с правильными значениями. Однако этот фрагмент можно еще улучшить, используя вместо этого метод insert . Приведенный выше код можно изменить, чтобы он выглядел следующим образом:

 $global wpdb; $post_id = $_POST [ 'post_id' ]; $meta_key = $_POST [ 'meta_key' ]; $meta_value = $_POST [ 'meta_value' ]; $wpdb ->insert( $wpdb ->postmeta, array ( 'post_id' => $_POST [ 'post_id' ], 'meta_key' => $_POST [ 'meta_key' ], 'meta_value' => $_POST [ 'meta_value' ] ) ); 

Если мы не предоставляем формат в качестве третьего параметра для метода insert , все данные, предоставленные во втором параметре, будут экранированы в виде строки. Кроме того, мы можем легко узнать, что этот код делает с первого взгляда, поскольку имя метода более понятно.

Правильная отладка запросов к базе данных

По умолчанию отчеты об ошибках отключены. Однако wpdb предоставляет два метода, которые можно использовать для переключения состояния отчетов об ошибках.

Чтобы включить функцию сообщения об ошибках, просто запустите этот код.

 $wpdb ->show_errors(); 

И чтобы выключить его:

 $wpdb ->hide_errors(); 

Еще одна вещь, на которую стоит обратить внимание: если мы установим для WP_DEBUG и WP_DEBUG_DISPLAY значение true , метод show_errors будет вызываться автоматически. Существует еще один полезный метод, который может использоваться для print_error ошибок, а именно print_error :

 $wpdb ->print_error(); 

Как следует из названия, он будет отображать ошибку только для самого последнего запроса, независимо от состояния сообщения об ошибках.

Еще один SAVEQUERIES трюк — включить SAVEQUERIES в wp-config.php . Это сохранит все запросы к базе данных, которые выполняются, время выполнения и место, откуда он был первоначально вызван, в свойство под названием queries в классе wpdb .

Чтобы получить эти данные, мы можем сделать следующее:

 print_r( $wpdb ->queries ); 

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

В большинстве случаев этих функций будет достаточно для отладки того, что не так с нашим кодом. Однако для более обширной отладки и создания отчетов всегда есть плагин Query Monitor, который может помочь в отладке не только запросов к базе данных.

Защита запросов от потенциальных атак

Чтобы полностью защитить наш код от внедрения SQL-кода, wpdb также предоставляет другой полезный метод с именем prepare который будет принимать строку оператора SQL и данные, которые необходимо экранировать. Это актуально всякий раз, когда мы имеем дело с такими методами, как query или get_results .

 $wpdb ->prepare( $sql , $format ... ); 

Метод prepare поддерживает как синтаксис sprintf и vsprintf . Первый параметр, $sql является оператором SQL, который заполнен заполнителями. Эти заполнители могут существовать в трех разных форматах:

  • %s для строки
  • %d для целого числа
  • %f для плавания

$format может быть набором параметров для sprintf подобного синтаксиса или массивом параметров, которые будут использоваться для замены заполнителя в $sql . Метод вернет SQL с правильно экранированными данными.

Давайте посмотрим, как мы можем добиться процесса удаления meta_key в wp_postmeta для определенного идентификатора записи:

 $global wpdb; $post_id = $_POST [ 'post_id' ]; $key = $_POST [ 'meta_key' ]; $wpdb ->query( "DELETE FROM $wpdb->postmeta WHERE post_id = $post_id AND meta_key = $key" ); 

Обратите внимание, что это не рекомендуемый способ удаления записи в базе данных с помощью wpdb . Это потому, что мы оставляем код открытым для SQL-инъекций, поскольку пользовательский ввод не экранирован должным образом и не используется непосредственно в операторе DELETE .

Однако это можно легко исправить! Мы просто вводим метод prepare перед выполнением фактического запроса, чтобы сгенерированный SQL был безопасным для использования. Это можно проиллюстрировать в фрагменте ниже:

 $global wpdb; $post_id = $_POST [ 'post_id' ]; $key = $_POST [ 'meta_key' ]; $wpdb ->query( $wpdb ->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s" , $post_id , $key ) ); 

Подключение к отдельным базам данных

По умолчанию переменная $wpdb является экземпляром класса wpdb который подключается к базе данных WordPress, определенной в wp-config.php . Если мы хотим взаимодействовать с другими базами данных, мы можем создать другой экземпляр класса wpdb . Это очень get_results для нас, поскольку доступны такие методы, как insert , update и get_results .

Класс wpdb принимает в конструкции четыре параметра: имя пользователя, пароль, имя базы данных и хост базы данных в указанном порядке. Вот пример:

 $mydb = new wpdb( 'username' , 'password' , 'my_database' , 'localhost' ); // At this point, $mydb has access to the database and all methods // can be used as usual // Example query $mydb ->query( 'DELETE FROM external_table WHERE id = 1' ); 

Если мы используем одно и то же имя пользователя, пароль и хост базы данных, но нам нужно только изменить выбранную базу данных, есть удобный метод с именем select для глобальной переменной $wpdb . Это достигается внутренне с помощью функции mysql_select_db / mysqli_select_db .

 $wpdb ->select( 'my_database' ); 

Это также особенно полезно, когда мы хотим переключиться на другую базу данных WordPress, но все же хотим сохранить функциональность таких функций, как get_post_custom и другие.

Использование пользовательских таблиц базы данных

Таблицы WordPress по умолчанию обычно достаточно для обработки самых сложных операций. Используя пользовательские типы записей с метаданными записей, а также пользовательские таксономии и метаданные терминов, мы можем делать практически все без необходимости использования пользовательских таблиц.

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

  • Полный контроль над структурой данных. Не все типы данных вписываются в структуру post , поэтому, когда мы хотим сохранить данные, которые не имеют никакого смысла, в качестве настраиваемого типа записи, лучше использовать пользовательскую таблицу.
  • Разделение проблем — поскольку наши данные хранятся в пользовательской таблице, они не будут мешать wp_postmeta таблиц wp_posts или wp_postmeta в отличие от использования пользовательских типов wp_postmeta . Миграция наших данных на другую платформу проще, поскольку она не ограничивается тем, как WordPress структурирует свои данные.
  • Эффективность. Запрос данных из нашей конкретной таблицы будет определенно намного быстрее, чем wp_posts таблице wp_posts которая также содержит данные, не связанные с нашим плагином. Это очевидная проблема при использовании пользовательских типов wp_postmeta для хранения большого количества метаданных, которые могут раздувать таблицу wp_postmeta .

dbDelta на помощь

Вместо использования wpdb для создания пользовательской таблицы базы данных, рекомендуется использовать dbDelta для обработки всего вашего первоначального создания таблицы, а также обновления схемы таблицы. Это надежно, поскольку ядро ​​WordPress также использует эту функцию для обработки любых обновлений схемы базы данных от версии к версии, если таковые имеются.

Чтобы создать пользовательскую таблицу изначально при установке плагина, нам нужно подключить нашу функцию к функции register_activation_hook . Предполагая, что наш основной файл плагина — plugin-name.php внутри plugin-name.php plugin-name , мы можем поместить эту строку прямо в него:

 register_activation_hook( __FILE__ , 'prefix_create_table' ); 

Далее нам нужно создать функцию prefix_create_table которая будет фактически создавать таблицу при активации плагина. Например, мы можем создать пользовательскую таблицу my_custom_table которая будет использоваться для хранения простых данных клиентов, таких как имя, фамилия и их адрес электронной почты.

 function prefix_create_table () { global $wpdb ; $charset_collate = $wpdb ->get_charset_collate(); $sql = "CREATE TABLE my_custom_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, first_name varchar(55) NOT NULL, last_name varchar(55) NOT NULL, email varchar(55) NOT NULL, UNIQUE KEY id (id) ) $charset_collate;" ; if ( ! function_exists( 'dbDelta' ) ) { require_once ( ABSPATH . 'wp-admin/includes/upgrade.php' ); } dbDelta( $sql ); } 

Чтобы максимизировать совместимость, мы извлекаем сопоставление кодировки базы данных из wpdb . Кроме того, оператор SQL должен соблюдать некоторые правила, чтобы убедиться, что он работает как задумано. Это взято непосредственно со страницы Кодекса в Создание таблиц с плагином :

  • Вы должны поместить каждое поле в отдельной строке в своем выражении SQL.
  • У вас должно быть два пробела между словами PRIMARY KEY и определением вашего первичного ключа.
  • Вы должны использовать ключевое слово KEY, а не его синоним INDEX, и вы должны включить хотя бы один KEY.
  • Вы не должны использовать апострофы или обратные метки вокруг имен полей.
  • Все типы полей должны быть строчными.
  • Ключевые слова SQL, такие как CREATE TABLE и UPDATE, должны быть в верхнем регистре.
  • Вы должны указать длину всех полей, которые принимают параметр длины. int (11), например.

Также, как правило, рекомендуется хранить версию базы данных в таблице параметров, чтобы мы могли сравнить их во время обновления плагина в случае необходимости обновления нашей пользовательской таблицы. Для этого мы просто добавляем эту строку сразу после создания нашей таблицы с dbDelta функции dbDelta :

 add_option( 'prefix_my_plugin_db_version' , '1.0' ); 

Обновление схемы таблицы

Используя тот же пример, что и выше, скажем, во время разработки, мы передумали, и мы также хотим сохранить номер телефона нашего клиента в нашей таблице. Что мы можем сделать, так это запустить обновление схемы таблицы во время обновления нашего плагина. Поскольку register_activation_hook не будет запущен во время обновления плагина, мы можем вместо этого подключиться к действию plugin_loaded , чтобы выполнить проверку версии нашей базы данных и обновить схему таблицы, если это необходимо.

Сначала мы добавляем нашу пользовательскую функцию обновления в plugin_loaded :

 add_action( 'plugin_loaded' , 'prefix_update_table' ); 

Реальные функции должны сделать несколько вещей:

  1. Нам нужно получить сохраненную версию базы данных.
  2. Сравните их с нашей текущей версией базы данных.
  3. Если это новее, мы снова dbDelta функцию dbDelta .
  4. Наконец, мы сохраняем обновленную версию базы данных в таблице параметров.

По большей части мы можем фактически использовать функцию prefix_create_table как мы делали выше, с некоторыми незначительными изменениями:

 function prefix_update_table () { // Assuming we have our current database version in a global variable global $prefix_my_db_version ; // If database version is not the same if ( $prefix_my_db_version != get_option( 'prefix_my_plugin_db_version' ) { global $wpdb ; $charset_collate = $wpdb ->get_charset_collate(); $sql = "CREATE TABLE my_custom_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, first_name varchar(55) NOT NULL, last_name varchar(55) NOT NULL, phone varchar(32) DEFAULT '' NOT NULL, //new column email varchar(55) NOT NULL, UNIQUE KEY id (id) ) $charset_collate;" ; if ( ! function_exists( 'dbDelta' ) ) { require_once ( ABSPATH . 'wp-admin/includes/upgrade.php' ); } dbDelta( $sql ); update_option( 'prefix_my_plugin_db_version' , $prefix_my_db_version ); } } 

Обратите внимание, что нам не нужно использовать оператор ALTER , поскольку dbDelta возьмет наш оператор SQL, сравнит его с существующими таблицами и внесет соответствующие изменения. Довольно удобно!

Вывод

WordPress не ограничивается созданием простых веб-сайтов, так как он быстро переходит на полноценную среду приложений. Расширение WordPress с помощью пользовательских типов записей и пользовательских таксономий должно быть нашим главным приоритетом. Однако, когда нам необходимо более точное управление нашими данными, обнадеживает тот факт, что WordPress сам предоставляет различные функции и классы, такие как wpdb для использования разработчиками. Это то, что делает WordPress зрелым решением.