WordPress как на ладони
wordpress jino

Блок произвольных полей в админке WordPress своими руками

wordpress против
Оглавление:

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

Чтобы вывести подобный блок для элементов таксономий, смотрите описание события: taxonomy-_edit_form_fields. Также смотрите ответ на вопрос: Метаполя для рубрик (таксономий) в WordPress

Вводная часть

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

Каждый, кто достаточно близко знаком с WordPress неоднократно встречался с понятием «произвольные поля» и с их помощью решал некоторые нетривиальные задачи.

Произвольные поля в WordPress — очень удобный инструмент, когда нужно «прикрепить» к конкретному посту какие-либо дополнительные данные. Такими данными может быть что угодно, начиная от логических true/false (1/0), заканчивая объемными текстами, массивами и прочим. К примеру, мы можем создать новое произвольное поле Title и в его значение написать текст (альтернативный заголовок поста), затем в коде шаблона использовать следующий код, чтобы вывести этот текст:

<title><?php echo get_post_meta($post->ID, 'title', true); ?></title>

Следует отметить, что функцию get_post_meta() можно использовать за пределами Цикла WordPress, т.е. где угодно в шаблоне. В данном примере мы используем её в <head> части документа, чтобы дать html странице заголовок отличный от заголовка самой статьи (иногда полезно для SEO).

Блок произвольных полей в WordPress

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

Произвольные поля используются в WordPress сплошь и рядом, различными плагинами оценки постов (WP-PostRatings), SEO плагинами (Platinum SEO Pack), позволяющими указать Title, Description, Keywords поста, моим плагином для создания миниатюр (Kama Thumbnail) и многими другими плагинами. Образно говоря, каждая четвертая нестандартная задача решается посредством произвольных полей, поэтому если вы еще не знаете как их использовать, то ознакомьтесь с этим мануалом. А ниже мы поговорим о том, как создать отдельный блок с нужными нам произвольными полями и как сделать это без плагинов.

Мало кто знает, что если создать произвольное поле ключ которого (название) начинается на _ (нижнее подчеркивание), например _my_special_key, то такое поле не будет выводиться в выпадающем списке произвольных полей при редактировании постов и будет считаться "внутренним" произвольным полем, которое используется системой. Создать такое поле можно только запросом к БД, например, используя функции add_post_meta() или update_post_meta().

к началу

Прежде чем начать создание

Забегая вперед, скажу что нижеследующие описание будет полезно, только тем кто хоть немного разбирается в HTML, т.е. если вы совсем не в зуб ногой в HTML и PHP, то читать все что ниже — пустая трата времени.

Предположим, что мы делаем сайт на заказ, и при этом на сайте используются произвольные поля. Объяснять заказчику, какое поле выбирать из списка, чтобы добавить ту или иную информацию к посту, проблематично, к тому же это быстро забывается. Именно поэтому уже давно написаны плагины, благодаря которым можно легко создать блок произвольных полей, где не нужно выбирать поле (ключ), и только потом вписывать значение. В таких блоках нужно сразу вписывать значение и есть возможность описать каждое поле, при одном взгляде на которые становится понятно его назначение. Блок, который мы сейчас создадим будет выглядеть так:

Кастомный блок произвольных полей

А данные такого блока в самих произвольных полях, после сохранения поста, будут записаны так:

Произвольные поля в WordPress

Что мы видим? — Ненужные для посторонних глаз ключи произвольных полей (о них я говорил выше), которые к тому же нужно еще и выбирать из выпадающего списка (а их там может быть совсем не 4, а куда больше...): description, robotmeta, select и title. Разумеется, создать мета блок произвольных полей — отличная идея.

к началу

Создаем мета блок произвольных полей

Для создания метаблока нам понадобятся всего 2 хука: add_meta_boxes и save_post, функция add_meta_box() и некоторые знания html и php. Добавляем следующий код в файл темы functions.php:

#1. Создадим новый мета блок для постов

Назовем его "Дополнительные поля":

// подключаем функцию активации мета блока (my_extra_fields)
add_action('add_meta_boxes', 'my_extra_fields', 1);

function my_extra_fields() {
	add_meta_box( 'extra_fields', 'Дополнительные поля', 'extra_fields_box_func', 'post', 'normal', 'high'  );
}

#2. Заполним этот блок полями html формы.

Делается это через, указанную в add_meta_box() функцию extra_fields_box_func(). Именно она отвечает за содержание мета блока:

<?php
// код блока
function extra_fields_box_func( $post ){
	?>
	<p><label><input type="text" name="extra[title]" value="<?php echo get_post_meta($post->ID, 'title', 1); ?>" style="width:50%" /> ? заголовок страницы (title)</label></p>

	<p>Описание статьи (description):
		<textarea type="text" name="extra[description]" style="width:100%;height:50px;"><?php echo get_post_meta($post->ID, 'description', 1); ?></textarea>
	</p>

	<p>Видимость поста: <?php $mark_v = get_post_meta($post->ID, 'robotmeta', 1); ?>
		 <label><input type="radio" name="extra[robotmeta]" value="" <?php checked( $mark_v, '' ); ?> /> index,follow</label>
		 <label><input type="radio" name="extra[robotmeta]" value="nofollow" <?php checked( $mark_v, 'nofollow' ); ?> /> nofollow</label>
		 <label><input type="radio" name="extra[robotmeta]" value="noindex" <?php checked( $mark_v, 'noindex' ); ?> /> noindex</label>
		 <label><input type="radio" name="extra[robotmeta]" value="noindex,nofollow" <?php checked( $mark_v, 'noindex,nofollow' ); ?> /> noindex,nofollow</label>
	</p>

	<p><select name="extra[select]">
			<?php $sel_v = get_post_meta($post->ID, 'select', 1); ?>
			<option value="0">----</option>
			<option value="1" <?php selected( $sel_v, '1' )?> >Выбери меня</option>
			<option value="2" <?php selected( $sel_v, '2' )?> >Нет, меня</option>
			<option value="3" <?php selected( $sel_v, '3' )?> >Лучше меня</option>
		</select> ? выбор за вами</p>

	<input type="hidden" name="extra_fields_nonce" value="<?php echo wp_create_nonce(__FILE__); ?>" />
	<?php
}

Все названия полей я оформил в массив extra[], чтобы потом проще было обработать эти данные.

Спрятанное поле name="extra_fields_nonce", нужно для проверки при сохранении данных.

#3. Сохраняем данные

На этом этапе, мы уже создали блок произвольных полей, теперь нужно обработать данные полей при сохранении поста. Обработать, значит записать их в в базу данных или удалить от туда. Для этого используем хук save_post, который срабатывает в момент сохранения поста. В этот момент мы получим данные из массива extra[] и обработаем них:

// включаем обновление полей при сохранении
add_action('save_post', 'my_extra_fields_update', 0);

/* Сохраняем данные, при сохранении поста */
function my_extra_fields_update( $post_id ){
	if ( ! wp_verify_nonce($_POST['extra_fields_nonce'], __FILE__) ) return false; // проверка
	if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE  ) return false; // выходим если это автосохранение
	if ( !current_user_can('edit_post', $post_id) ) return false; // выходим если юзер не имеет право редактировать запись

	if( !isset($_POST['extra']) ) return false; // выходим если данных нет

	// Все ОК! Теперь, нужно сохранить/удалить данные
	$_POST['extra'] = array_map('trim', $_POST['extra']); // чистим все данные от пробелов по краям
	foreach( $_POST['extra'] as $key=>$value ){
		if( empty($value) ){
			delete_post_meta($post_id, $key); // удаляем поле если значение пустое
			continue;
		}

		update_post_meta($post_id, $key, $value); // add_post_meta() работает автоматически
	}
	return $post_id;
}

Вот и все, блок произвольных полей готов!

Теперь, изменяя html код, мы можем редактировать содержимое мета блока. Но не забываем, что названия полей имеют вид массива со значением ключа произвольно поля: name="extra[meta_key]".

Весь код, целиком

<?php
// подключаем функцию активации мета блока (my_extra_fields)
add_action('add_meta_boxes', 'my_extra_fields', 1);

function my_extra_fields() {
	add_meta_box( 'extra_fields', 'Дополнительные поля', 'extra_fields_box_func', 'post', 'normal', 'high'  );
}

// код блока
function extra_fields_box_func( $post ){
	?>
	<p><label><input type="text" name="extra[title]" value="<?php echo get_post_meta($post->ID, 'title', 1); ?>" style="width:50%" /> ? заголовок страницы (title)</label></p>

	<p>Описание статьи (description):
		<textarea type="text" name="extra[description]" style="width:100%;height:50px;"><?php echo get_post_meta($post->ID, 'description', 1); ?></textarea>
	</p>

	<p>Видимость поста: <?php $mark_v = get_post_meta($post->ID, 'robotmeta', 1); ?>
		 <label><input type="radio" name="extra[robotmeta]" value="" <?php checked( $mark_v, '' ); ?> /> index,follow</label>
		 <label><input type="radio" name="extra[robotmeta]" value="nofollow" <?php checked( $mark_v, 'nofollow' ); ?> /> nofollow</label>
		 <label><input type="radio" name="extra[robotmeta]" value="noindex" <?php checked( $mark_v, 'noindex' ); ?> /> noindex</label>
		 <label><input type="radio" name="extra[robotmeta]" value="noindex,nofollow" <?php checked( $mark_v, 'noindex,nofollow' ); ?> /> noindex,nofollow</label>
	</p>

	<p><select name="extra[select]" />
			<?php $sel_v = get_post_meta($post->ID, 'select', 1); ?>
			<option value="0">----</option>
			<option value="1" <?php selected( $sel_v, '1' )?> >Выбери меня</option>
			<option value="2" <?php selected( $sel_v, '2' )?> >Нет, меня</option>
			<option value="3" <?php selected( $sel_v, '3' )?> >Лучше меня</option>
		</select> ? выбор за вами</p>

	<input type="hidden" name="extra_fields_nonce" value="<?php echo wp_create_nonce(__FILE__); ?>" />
	<?php
}

// включаем обновление полей при сохранении
add_action('save_post', 'my_extra_fields_update', 0);

/* Сохраняем данные, при сохранении поста */
function my_extra_fields_update( $post_id ){
	if ( !isset($_POST['extra_fields_nonce']) || !wp_verify_nonce($_POST['extra_fields_nonce'], __FILE__) ) return false; // проверка
	if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE  ) return false; // если это автосохранение
	if ( !current_user_can('edit_post', $post_id) ) return false; // если юзер не имеет право редактировать запись

	if( !isset($_POST['extra']) ) return false; 

	// Все ОК! Теперь, нужно сохранить/удалить данные
	$_POST['extra'] = array_map('trim', $_POST['extra']);
	foreach( $_POST['extra'] as $key=>$value ){
		if( empty($value) ){
			delete_post_meta($post_id, $key); // удаляем поле если значение пустое
			continue;
		}

		update_post_meta($post_id, $key, $value); // add_post_meta() работает автоматически
	}
	return $post_id;
}
к началу

Блок произвольных полей для произвольного типа записей

Если нужно создать блок для другого типа записей, допустим page (для страниц), то регистрируем еще один мета блок и описываем его html код в новой функции, которую так же нужно указать при регистрации блока (extra_fields_box_page_func). Функцию обработки полей при сохранении поста создавать уже не надо, главное указать названия полей в виде массивов extra[]:

function my_extra_fields() {
	add_meta_box( 'extra_fields', 'Дополнительные поля', 'extra_fields_box_func', 'post', 'normal', 'high'  );
	add_meta_box( 'extra_fields', 'Дополнительные поля', 'extra_fields_box_page_func', 'page', 'normal', 'high'  );
}

// html код блока для типа записей page
function extra_fields_box_page_func(){
   ?>
   ---------- Здесь поля html формы -----------
   <?php
}
к началу

Сложности с типом checkbox

Недостатком такого метода является то, что массив extra[], обязательно должен быть определен, пусть даже он передает пустое значение иначе поле не будет обработано при сохранении данных. В связи с этим, возникает проблема при использовании типа checkbox: <input type="checkbox", потому что checkbox передает данные только, если галочка выставлена и вообще ничего не передает, если галки нет. А нам нужно чтобы он передавал пустое значение, чтобы код удалял значение, если оно было сохранено до этого.

Чтобы обойти этот "недуг" я сделал так: перед полем чекбокса создаем hidden поле с name как у чекбокса и пустым значением. И получается, если галочка стоит, то значение hidden поля перебивается, если галки нет, то берется пустое значение hidden поля.

Т.е. checkbox нужно вызывать так:

<input type="hidden" name="extra[my_checkbox]" value="" />
<input type="checkbox" name="extra[my_checkbox]" value="1" />

Такой же трюк иногда может пригодится и для поля с типом radio.

Пример реального кода с типами checkbox:

<?php 
// подключаем функцию активации мета блока (my_extra_fields)
add_action('admin_init', 'my_extra_fields', 1);

function my_extra_fields() {
	add_meta_box( 'extra_fields', 'Дополнительные поля', 'extra_fields_box_func', 'post', 'normal', 'high'  );
}

// код блока
function extra_fields_box_func( $post ){
?>
	<p><label><input type="text" name="extra[title]" value="<?php echo get_post_meta($post->ID, 'title', 1); ?>" style="width:50%" /> ? заголовок страницы (title)</label></p>

	<p>Описание статьи (description):
		<textarea type="text" name="extra[description]" style="width:100%;height:50px;"><?php echo get_post_meta($post->ID, 'description', 1); ?></textarea>
	</p>

	<p>Видимость поста: <?php $mark_v = get_post_meta($post->ID, 'robotmeta', 1); ?>
		<label><input type="radio" name="extra[robotmeta]" value="" <?php checked( $mark_v, '' ); ?> /> index,follow</label>
		<label><input type="radio" name="extra[robotmeta]" value="nofollow" <?php checked( $mark_v, 'nofollow' ); ?> /> nofollow</label>
		<label><input type="radio" name="extra[robotmeta]" value="noindex" <?php checked( $mark_v, 'noindex' ); ?> /> noindex</label>
		<label><input type="radio" name="extra[robotmeta]" value="noindex,nofollow" <?php checked( $mark_v, 'noindex,nofollow' ); ?> /> noindex,nofollow</label>
	</p>

	<p><select name="extra[select]" />
			<?php $sel_v = get_post_meta($post->ID, 'select', 1); ?>
			<option value="0">----</option>
			<option value="1" <?php selected( $sel_v, '1' )?> >Выбери меня</option>
			<option value="2" <?php selected( $sel_v, '2' )?> >Нет, меня</option>
			<option value="3" <?php selected( $sel_v, '3' )?> >Лучше меня</option>
		</select> ? выбор за вами</p>

	<p>
		<input type="hidden" name="extra[white]" value="">
		<label><input type="checkbox" name="extra[white]" value="1" <?php checked( get_post_meta($post->ID, 'white', 1), 1 )?> /> белый</label>
		<input type="hidden" name="extra[red]" value="">
		<label><input type="checkbox" name="extra[red]" value="1"   <?php checked( get_post_meta($post->ID, 'red',   1), 1 )?> /> красный</label>
		<input type="hidden" name="extra[black]" value="">
		<label><input type="checkbox" name="extra[black]" value="1" <?php checked( get_post_meta($post->ID, 'black', 1), 1 )?> /> черный</label>
	</p>

	<input type="hidden" name="extra_fields_nonce" value="<?php echo wp_create_nonce(__FILE__); ?>" />
<?php
}

// включаем обновление полей при сохранении
add_action('save_post', 'my_extra_fields_update', 0);

/* Сохраняем данные, при сохранении поста */
function my_extra_fields_update( $post_id ){
	if ( ! wp_verify_nonce($_POST['extra_fields_nonce'], __FILE__) ) return false; // проверка
	if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE  ) return false; // если это автосохранение
	if ( ! current_user_can('edit_post', $post_id) ) return false; // если юзер не имеет право редактировать запись

	if( !isset($_POST['extra']) ) return false;

	// Все ОК! Теперь, нужно сохранить/удалить данные
	$_POST['extra'] = array_map('trim', $_POST['extra']);
	foreach( $_POST['extra'] as $key=>$value ){
		if( empty($value) )
			delete_post_meta($post_id, $key); // удаляем поле если значение пустое
			continue;

		update_post_meta( $post_id, $key, $value ); // add_post_meta() работает автоматически
	}
	return $post_id;
}
к началу

Заключение

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

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

Это скорее обучающая статья и для многих плагины реализующие эту задачу будут лучшим решением.

к началу

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

В статье я говорил о плагинах, которые создают мета блоки заменяющие произвольные поля, но ни разу не упомянул ни один. Исправляюсь:

  • More Fields — очень простой и крутой плагин!

  • Advanced Custom Fields (ACF) — пожалуй, самый популярный и гибкий плагин для создания произвольных полей. С хорошей документацией.

  • Custom Field Template — настоящий комбайн. С ним можно создать любую форму, для любых типов постов, указать формы для отдельных постов и рубрик. Думаю в большинстве случаев,  можно обойтись без такого комбайна.

  • Magic-fields — отличный плагин, но очень объемный (весит как пол WordPress), благодаря которому можно создавать целые произвольные страницы создания новой записи. При создании такой страницы в плагине, можно выбирать какие блоки будут на ней использоваться. Так, например, можно создать страницу где будет вводится только заголовок и нужные произвольные поля. В какую категорию попадает запись после публикации с такой страницы задается заранее. Этот плагин заслуживает отдельного описания.

  • kc-settings - ураган а не плагин, хоть и не сторонник плагинов, но рекомендую.
Блок произвольных полей в админке WordPress своими руками 204 комментария
Полезные 19 Вопросы 7 Все
  • Дмитрий

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

    Ответить14 дней назад #
  • Игорь
    @

    Доброго времени суток. Использую несколько дополнительных полей type="text" с одинаковым ключом. Возникла проблема при сохранении. Как сделать, что бы поля сохранялись и редактировались с одинаковыми ключами? Заранее спасибо!

    Ответить10 дней назад #

Здравствуйте, !

Ваш комментарий