Метаданные в таксономиях без использования плагинов

Я уже писал про добавление метабоксов к постам (записям, страницам, произвольным типам постов), теперь давайте разберемся, как добавлять произвольные поля (дополнительные настройки) на страницы редактирования рубрик, меток и прочих таксономий, которые вы можете насоздавать.

Шаг 1. Создание таблицы в базе данных

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

Ну что же, похоже, что единственный выход — это создание собственной таблицы в БД, наподобие wp_postmeta.

Вообще назвать нужно её wp_termmeta (естественно префикс может быть другим), иначе стандартные вордпрессовские функции по работе с метаданными у нас просто не будут работать.

Есть два способа создания таблицы:

  1. Лезем в phpMyAdmin и создаем там.
  2. Через SQL-запрос, который можно задействовать прямо в коде.

Способ 1. Создание таблицы в базе данных через phpMyAdmin

Открываем phpMyAdmin, выбираем базу данных своего сайта, затем нажимаем на ссылку иконка создания новой таблицы. Настройки выставляем примерно следующие, одним словом ориентируемся на таблицу wp_postmeta.

создание новой таблицы через phpMyAdmin

Способ 2. Использование SQL-запроса для создания таблицы

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

if ( is_admin() && isset($_GET['activated'] ) && $pagenow == "themes.php" ) { // вешаем запрос создания таблицы на активацию темы
	global $wpdb; // глобальный класс $wpdb необходим при работе с базой данных в WordPress
	$collate = '';
 
	if($wpdb->has_cap('collation')) { 
		if(!empty($wpdb->charset)) $collate = "DEFAULT CHARACTER SET $wpdb->charset";
		if(!empty($wpdb->collate)) $collate .= " COLLATE $wpdb->collate";
	}
 
	// дальше идет сам SQL запрос
	$wpdb->query("CREATE TABLE IF NOT EXISTS ". $wpdb->prefix . "termmeta (
		`meta_id` bigint(20) NOT NULL AUTO_INCREMENT,
		`term_id` bigint(20) NOT NULL,
		`meta_key` varchar(255) NOT NULL,
		`meta_value` longtext DEFAULT '' NOT NULL,
		PRIMARY KEY id (`meta_id`)) $collate;"
	);
}

После того, как таблица будет создана, можно будет перейти к следующему шагу.

Шаг 2. Универсальное решение по добавлению настроек — использование PHP класса

Реально — использование готового класса это круто и легко. Вы на лету сможете создавать столько настроек в таксономиях, сколько захотите.

И я дам вам готовый класс с некоторыми базовыми полями: <input> (текстовый и чекбокс), <select>, <textarea>. Также вы можете прикрутить поле с загрузкой изображения, используя код из этого поста.

Я решил не добавлять в код вообще ничего лишнего (я не хочу сказать, что nonce-проверки и проверки на права пользователей — лишнее, просто в этом примере я от них отказался для простоты восприятия кода).

<?php
class trueTaxonomyMetaBox {
	/*
	 * Функция-конструктор, в ней мы активируем хуки
	 */
	function __construct( $options ) {
		$this->options = $options;
		$this->prefix = $this->options['id'] .'_'; // префикс настроек
		foreach ( $this->options['taxonomy'] as $taxonomy ) { // для каждой таксономии, которая указана в параметрах
			add_action($taxonomy . '_edit_form_fields', array(&$this, 'fill'), 10, 2 ); // хук добавления полей
		}
		add_action('edit_term', array(&$this, 'save'), 10,1); // хук сохранения значений полей
	}
	/*
	 * Функция, создающая поля, я описал в ней текстовые поля (input type=text и textarea), чекбокс и выпадающий список
	 */
	function fill( $term, $taxonomy ){
 
		foreach ( $this->options['args'] as $param ) { // для каждого описанного параметра...
 
			?><tr class="form-field"><?php
				// определяем значение произвольного поля таксономии
				if(!$value = get_metadata('term', $term->term_id, $this->prefix .$param['id'], true)) $value = $param['std'];
				switch ( $param['type'] ) {
					// input[type="text"]
					case 'text':{ ?>
						<th scope="row"><label for="<?php echo $this->prefix .$param['id'] ?>"><?php echo $param['title'] ?></label></th>
						<td>
							<input name="<?php echo $this->prefix .$param['id'] ?>" type="<?php echo $param['type'] ?>" id="<?php echo $this->prefix .$param['id'] ?>" value="<?php echo $value ?>" class="regular-text" />
							<?php if(isset( $param['desc'] ) ) echo '<p class="description">' . $param['desc'] . '</p>'  ?>
						</td>
						<?php
						break;							
					}
					// textarea
					case 'textarea':{ ?>
						<th scope="row"><label for="<?php echo $this->prefix .$param['id'] ?>"><?php echo $param['title'] ?></label></th>
						<td>
							<textarea name="<?php echo $this->prefix .$param['id'] ?>" type="<?php echo $param['type'] ?>" id="<?php echo $this->prefix .$param['id'] ?>" value="<?php echo $value ?>" class="large-text" /><?php echo $value ?></textarea>
							<?php if(isset( $param['desc'] ) ) echo '<p class="description">' . $param['desc'] . '</p>'  ?>
						</td>
						<?php
						break;							
					}
					// input[type="checkbox"]
					case 'checkbox':{ ?>
						<th scope="row"><label for="<?php echo $this->prefix .$param['id'] ?>"><?php echo $param['title'] ?></label></th>
						<td>
							<label for="<?php echo $this->prefix .$param['id'] ?>"><input name="<?php echo $this->prefix .$param['id'] ?>" type="<?php echo $param['type'] ?>" id="<?php echo $this->prefix .$param['id'] ?>"<?php echo ($value=='on') ? ' checked="checked"' : '' ?> />
							<?php if(isset( $param['desc'] ) ) echo $param['desc']  ?></label>
						</td>
						<?php
						break;							
					}
					// select
					case 'select':{ ?>
						<th scope="row"><label for="<?php echo $this->prefix .$param['id'] ?>"><?php echo $param['title'] ?></label></th>
						<td>
							<label for="<?php echo $this->prefix .$param['id'] ?>">
							<select name="<?php echo $this->prefix .$param['id'] ?>" id="<?php echo $this->prefix .$param['id'] ?>"><option>...</option><?php
								foreach($param['args'] as $val=>$name){
									?><option value="<?php echo $val ?>"<?php echo ( $value == $val ) ? ' selected="selected"' : '' ?>><?php echo $name ?></option><?php
								}
							?></select></label>
							<?php if(isset( $param['desc'] ) ) echo '<p class="description">' . $param['desc'] . '</p>'  ?>
						</td>
						<?php
						break;							
					}
				} 
			?></tr><?php
 
		}
 
	}
	/*
	 * Функция сохранения значений полей
	 */
	function save( $term_id ){
		foreach ( $this->options['args'] as $param ) {
			if ( isset( $_POST[ $this->prefix . $param['id'] ] ) && trim( $_POST[ $this->prefix . $param['id'] ] ) ) {
				update_metadata('term', $term_id, $this->prefix . $param['id'], trim($_POST[ $this->prefix . $param['id'] ], '') );
			} else {
				delete_metadata('term', $term_id, $this->prefix . $param['id'], '', false);
			}
		}
	}
}

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

Вот пример:

$options = array(
	array(
		'id'	=>	'metatax', // айдишник играет роль префикса названий полей
		'taxonomy'	=>	array('category', 'game'), // укажите таксономии, для которых нужно добавить ниже перечисленные поля
		'args'	=>	array(
			array(
				'id'			=>	'order', // атрибуты name и id без префикса, например с префиксом будет meta1_field_1
				'title'			=>	'Порядок', // лейбл поля
				'type'			=>	'text', // тип, в данном случае обычное текстовое поле
				'desc'			=>	'Укажите порядок сортировки.', // описание
				'std'			=>	'0' // значение поля по умолчанию
			),
			array(
				'id'			=>	'homepage',
				'title'			=>	'Главная',
				'type'			=>	'checkbox', // чекбокс
				'desc'			=>	'Отображать на главной странице в списке рубрик',
				'std'			=>	''
			)
		)
	),
	array(
		'id'	=>	'metagame',
		'taxonomy'	=>	array('game'),
		'args'	=>	array(
			array(
				'id'			=>	'genre',
				'title'			=>	'Жанр игры',
				'type'			=>	'select', // выпадающий список
				'desc'			=>	'Выберите жанр из выпадающего списка.',
				'std'			=>	'',
				'args'			=>	array('mmorpg' => 'MMORPG', 'strategy' => 'Стратегия', 'racing' => 'Гонки' ) // элементы задаются через массив, по типу атрибут value=>лейбл
			),
			array(
				'id'			=>	'reqs',
				'title'			=>	'Системные требования',
				'type'			=>	'textarea',
				'std'			=>	''
			)
		)
	)
);
 
foreach ($options as $option) {
	$truetaxmetabox = new trueTaxonomyMetaBox($option);
}

Вот результат:

добавленные поля

Вот в принципе и всё. Я написал только про самое основное. Если вы не хотите со всем этим заморачиваться, либо у вас что-то не получается, либо вам нужны расширенные возможности (например загрузка изображений к таксономиям), то могу порекомендовать готовый класс, который я сделал в виде плагина WP.

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

Подпишитесь, чтобы раз в неделю получать свежие статьи с блога по email.

По теме

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

  • Надежда Жук5 июня 2015 в 16:06 #

    Здравствуйте Михаил! У вас замечательный и полезный сайт! Я изучаю программирование сама и столкнулась с проблемой, которую никак не могу реализовать. Мне нужно написать плагин для WordPres с помощью которого можно будет реализовывать схему для страниц и постов WordPress в админке которая добавляет метабокс на страницу в котором можно задавать: название услуги\товара, описание услуги\товара, изображение услуги\товара, цену услуги\товара, валюту услуги\товара так же плагин создает виджет который выводит указанные данные если они заданы для страницы на которой он вызывается. если не заданы, то просто не выводит виджет на этой странице
    так же плагин создает шорткод который можно вставить в текст страницы и который будет выводить те же данные в виде блока. (шорткод принимает значение в виде id записи\поста, если значение на задано, то выводит данные той записи из которой вызван). Вы не могли бы как будет время создать пост на эту тему ? Заранее спасибо

  • Надежда Жук6 июня 2015 в 12:06 #

    Спасибо Михаил!

  • Alexandr18 июля 2015 в 04:07 #

    Как к этим данным потом обращаться из кода?

  • Семен Астахов1 сентября 2015 в 11:09 #

    В версии WP 4.3 не работает.
    Выяснил, что _get_meta_table отдает false и соответственно прекращается работа функции update.

    function _get_meta_table($type) {
        global $wpdb;
     
        $table_name = $type . 'meta';
     
        if ( empty($wpdb->$table_name) )
            return false;
     
        return $wpdb->$table_name;
    }

    False отдает из-за того, что в $wpdb нет объекта termmeta. Решил добавлением public $termmeta; в wp-includes/wp-db.php

    Понятно, что при обновлении WP слетит всё. Может есть какое-то решение?

    • Миша2 сентября 2015 в 21:09 #

      Спасибо за замечание!
      Попробую доработать код.

      • Дима9 ноября 2015 в 16:11 #

        Извините, а код уже доработан?

      • Семен Астахов13 ноября 2015 в 18:11 #

        Не работали над этим?

        Если нет, то можете подсказать в каком направлении думать? Вновь столкнулся с этой проблемой и хотелось бы сделать универсальное решение.

        • Миша14 ноября 2015 в 09:11 #

          Пока не работал и даже направление хз какое. Но. В данный момент работаю над плагином метабоксов для постов, который будет также поддерживать возможность метабоксов для таксономий!

          По срокам. Ну к концу года думаю уже выпущу его.

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

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

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

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

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

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