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

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

Шаг 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);
}

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

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

Вот в принципе и всё. Я написал только про самое основное.

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

Миша Рудрастых Разработчик WordPress WooCommerce

Миша Рудрастых

Впервые познакомился с WordPress в 2009 году, и после двух лет мучений с Joomla и самописными движками это был просто бальзам на душу. С 2014 года меня можно встретить на WordCamp — официальной конфе по WP в Москве, иногда там выступаю. Также в настоящее время веду курсы по WordPress в Epic Skills в Питере.

По теме