WordPress как на ладони
Очень Удобный и Быстрый Хостинг для сайтов на WordPress. Пользуюсь сам и вам рекомендую!

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

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

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

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

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

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

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

К примеру, мы можем создать новое произвольное поле Title и в его значение написать текст (альтернативный заголовок поста), затем в коде шаблона использовать следующий код, чтобы вывести этот текст:

<h1><?php echo get_post_meta( $post_id, 'title', true ); ?></h1>

Функцию get_post_meta() можно использовать где угодно в шаблоне или плагине, т.е. за пределами Цикла WordPress.

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

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

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

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

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

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

Гораздо удобнее иметь блок, куда можно вписывать только значения, и где каждое поле будет иметь осмысленное название и описание. Блок, который мы сейчас создадим будет выглядеть так:

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

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

Для редактора блоков (гутенберг) также можно включить дефолтный блок произвольных полей:

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

Для создания мета-блока нам понадобятся:

Добавляем следующий код в файл темы functions.php или в плагин:

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

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

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

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

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

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

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

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

	<p>
		Видимость поста: <?php $mark_v = get_post_meta( $post->ID, 'robotmeta', true ) ?>
		<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', true ) ?>
			<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="<?= wp_create_nonce( 'extra_fields_nonce_id' ) ?>"/>
	<?php
}

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

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

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

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

// обновление полей при сохранении
add_action( 'save_post', 'my_extra_fields_save_on_update', 0 );

function my_extra_fields_save_on_update( $post_id ) {
	// базовая проверка
	if(
		empty( $_POST['extra'] )
		|| ! wp_verify_nonce( $_POST['extra_fields_nonce'], 'extra_fields_nonce_id' )
		|| wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id )
	){
		return false;
	}

	$extra = $_POST['extra'];

	// Все ОК! Теперь, нужно сохранить/удалить данные

	// Очищаем все данные
	$extra = array_map( 'sanitize_text_field', $extra );
	foreach( $extra as $key => $value ){
		// удаляем поле если значение пустое
		if( ! $value ){
			delete_post_meta( $post_id, $key );
		}
		else {
			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.

Создание метабокса используя PHP класс

Этот пример показывает как создать одно поле, в котором будет храниться массив данных. Массив можно расширять или уменьшать через нажатие на + или удалить (работает на скрипте).

Это пример создание поля "повторитель", как у плагина ACF (поле repeater в платной версии).

В результате получим такой метабокс:

GitHub
<?php

$my_metabox = new My_Best_Metaboxes( 'post' );
$my_metabox->init();

class My_Best_Metaboxes {

	public $post_type;

	static $meta_prefix = 'company_address';

	public function __construct( $post_type ) {
		$this->post_type = $post_type;
	}

	public function init() {
		add_action( 'add_meta_boxes', [ $this, 'add_metabox' ] );
		add_action( 'save_post_' . $this->post_type, [ $this, 'save_metabox' ] );
		add_action( 'admin_print_footer_scripts', [ $this, 'show_assets' ], 10, 999 );
	}

	## Добавляет матабоксы
	public function add_metabox() {
		add_meta_box( 'box_info_company', 'Информация о компании', [
			$this,
			'render_metabox',
		], $this->post_type, 'advanced', 'high' );
	}

	## Отображает метабокс на странице редактирования поста
	public function render_metabox( $post ) {
		?>
		<table class="form-table company-info">
			<tr>
				<th>
					Адреса компании <span class="dashicons dashicons-plus-alt add-company-address"></span>
				</th>
				<td class="company-address-list">
					<?php
					$input = '
					<span class="item-address">
						<input type="text" name="' . self::$meta_prefix . '[]" value="%s">
						<span class="dashicons dashicons-trash remove-company-address"></span>
					</span>
					';

					$addresses = get_post_meta( $post->ID, self::$meta_prefix, true );

					if( is_array( $addresses ) ){
						foreach( $addresses as $addr ){
							printf( $input, esc_attr( $addr ) );
						}
					}
					else{
						printf( $input, '' );
					}
					?>
				</td>
			</tr>
		</table>
		<?php
	}

	## Очищает и сохраняет значения полей
	public function save_metabox( $post_id ) {

		if( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ){
			return;
		}

		if( ! isset( $_POST[ self::$meta_prefix ] ) ){
			return;
		}

		$addresses = $_POST[ self::$meta_prefix ];

		// очистка
		$addresses = array_map( 'sanitize_text_field', $addresses );

		// уберем пустые адреса
		$addresses = array_filter( $addresses );

		if( $addresses ){
			update_post_meta( $post_id, self::$meta_prefix, $addresses );
		}
		else{
			delete_post_meta( $post_id, self::$meta_prefix );
		}
	}

	## Подключает скрипты и стили
	public function show_assets() {
		if( is_admin() && get_current_screen()->id == $this->post_type ){
			$this->show_styles();
			$this->show_scripts();
		}
	}

	## Выводит на экран стили
	public function show_styles() {
		?>
		<style>
			.add-company-address{
				color: #00a0d2;
				cursor: pointer;
			}

			.company-address-list .item-address{
				display: flex;
				align-items: center;
			}

			.company-address-list .item-address input{
				width: 100%;
				max-width: 400px;
			}

			.remove-company-address{
				color: brown;
				cursor: pointer;
			}
		</style>
		<?php
	}

	## Выводит на экран JS
	public function show_scripts() {
		?>
		<script>
			jQuery( document ).ready( function( $ ){

				var $companyInfo = $( '.company-info' );

				// Добавляет бокс с вводом адреса фирмы
				$( '.add-company-address', $companyInfo ).click( function(){
					var $list = $( '.company-address-list' );
					$item = $list.find( '.item-address' ).first().clone();

					$item.find( 'input' ).val( '' ); // чистим знанчение

					$list.append( $item );
				} );

				// Удаляет бокс с вводом адреса фирмы
				$companyInfo.on( 'click', '.remove-company-address', function(){
					if( $( '.item-address' ).length > 1 ){
						$( this ).closest( '.item-address' ).remove();
					}
					else{
						$( this ).closest( '.item-address' ).find( 'input' ).val( '' );
					}
				} );

			} );
		</script>
		<?php
	}

}

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

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

  • Custom Field Suite — похож на ACF, только менее навороченный.

  • CMB2 — CMB2 инструмент для разработчиков для создания: метабоксов, метаполей. Позволяет легко управлять записями, элементами таксономий, пользователями, комментариями или создавать произвольные страницы настроек.

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

Заключение

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

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

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

209 комментариев
Полезные 24Вопросы 1 Все
    Войти