WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru WPmentor - Ищем топовых специалистов по WordPress, чтобы помочь вам заработать

Блок произвольных полей в админке 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 (
		   empty( $_POST['extra'] )
		|| ! wp_verify_nonce( $_POST['extra_fields_nonce'], __FILE__ )
		|| wp_is_post_autosave( $post_id )
		|| wp_is_post_revision( $post_id )
	)
		return false;

	// Все ОК! Теперь, нужно сохранить/удалить данные
	$_POST['extra'] = array_map( 'sanitize_text_field', $_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]".

меню

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

Если нужно создать блок для другого типа записей, допустим 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 (
		   empty( $_POST['extra'] )
		|| ! wp_verify_nonce( $_POST['extra_fields_nonce'], __FILE__ )
		|| wp_is_post_autosave( $post_id )
		|| wp_is_post_revision( $post_id )
	)
		return false;

	// Все ОК! Теперь, нужно сохранить/удалить данные
	$_POST['extra'] = array_map( 'sanitize_text_field', $_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;
}
меню

Еще один пример создания метабокса (ООП)

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

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

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

<?php

new My_Best_Metaboxes;

class My_Best_Metaboxes {

	public $post_type = 'post';

	static $meta_key = 'company_address';

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

	## Добавляет матабоксы
	public function add_metabox() {
		add_meta_box( 'box_info_company', 'Информация о компании', array( $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_key .'[]" value="%s">
						<span class="dashicons dashicons-trash remove-company-address"></span>
					</span>
					';

					$addresses = get_post_meta( $post->ID, self::$meta_key, 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 ) {

		// Check if it's not an autosave.
		if ( wp_is_post_autosave( $post_id ) )
			return;

		if ( isset( $_POST[self::$meta_key] ) && is_array( $_POST[self::$meta_key] ) ) {
			$addresses = $_POST[self::$meta_key];

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

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

			if ( $addresses ) 
				update_post_meta( $post_id, self::$meta_key, $addresses );
			else 
				delete_post_meta( $post_id, self::$meta_key );

		}
	}

	## Подключает скрипты и стили
	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, только менее навороченный.

  • Carbon Fields — похож на ACF только без визуальной настройки, все делается в коде. Хорошо подойдет для разработчиков. Бесплатный.

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

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

  • kc-settings - ураган а не плагин, хоть и не сторонник плагинов, но рекомендую.
меню

Заключение

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

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

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

225 комментов
Полезные 23 Вопросы 6 Все
  • @ Юрий

    Как в phpMyAdmin создать произвольное поле "photobox" сразу для всех постов WordPress?

    -1
    Ответить06.Янв.2019 в 00:50 #
  • @ Андрей mskrepair.ru

    Доброго времени суток, подскажите пожалуйста можно ли вывести 2 строчки ( select и input ) ?
    Например в select будет уже готовый список а в input данные ( все это в одну строчку например ) и добавление тоже в 2 значения

    Все сделал, может быть можно уменьшить код? Посмотрите пожалуйста.

    new my_files_meta;
    
    class my_files_meta {
    
    	public $post_type = 'post';
    
    	static $files_type = 'files_type';
    	static $files_url = 'company_address';
    
    	public function __construct() {
    		add_action( 'add_meta_boxes', array( $this, 'add_metabox' ) );
    		add_action( 'save_post_' . $this->post_type, array( $this, 'save_metabox' ) );
    		add_action( 'admin_print_footer_scripts', array( $this, 'show_assets' ), 10, 999 );
    	}
    
    	## Добавляет матабоксы
    	public function add_metabox() {
    		add_meta_box( 'box_info_company', 'Предоставление файлов', array( $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"><select name="'. self::$files_type .'[]"><option value="">Тип формата</option><option value="word">Microsoft Word</option><option value="xls">Microsoft Excel</option><option value="ppt">Microsoft Power Point</option><option value="pdf">Adobe Reader</option><option value="zip">ZIP Архив</option><option value="jpg">Рисунок</option><option value="avi">Видео AVI</option><option value="mp4">Видео MP4</option></select><input type="text" name="'. self::$files_url .'[]" value="%s"><span class="dashicons dashicons-trash remove-company-address"></span><span></span></span>';
    
    					$files_urls = get_post_meta( $post->ID, self::$files_url, true );
    					$files_types = get_post_meta( $post->ID, self::$files_type, true );
    
    					if ( is_array( $files_urls ) ) {
    						$i=0;
    						foreach ( $files_urls as $addr ) {
    							// echo $files_types[$i];
    							if ($files_types[$i] == "word") { $sword = " selected"; }
    							else if ($files_types[$i] == "xls") { $sxls = " selected"; }
    							else if ($files_types[$i] == "ppt") { $sppt = " selected"; }
    							else if ($files_types[$i] == "pdf") { $spdf = " selected"; }
    							else if ($files_types[$i] == "zip") { $szip = " selected"; }
    							else if ($files_types[$i] == "jpg") { $sjpg = " selected"; }
    							else if ($files_types[$i] == "avi") { $savi = " selected"; }
    							else if ($files_types[$i] == "mp4") { $smp4 = " selected"; }
    							// printf( $input, esc_attr( $addr ) );
    							echo '<span class="item-address"><select name="'. self::$files_type .'[]"><option value="">Тип формата</option><option value="word" ' . $sword . '>Microsoft Word</option><option value="xls" ' . $sxls . '>Microsoft Excel</option><option value="ppt" ' . $sppt . '>Microsoft Power Point</option><option value="pdf" ' . $spdf . '>Adobe Reader</option><option value="zip" ' . $szip . '>ZIP Архив</option><option value="jpg" ' . $sjpg . '>Рисунок</option><option value="avi" ' . $savi . '>Видео AVI</option><option value="mp4" ' . $smp4 . '>Видео MP4</option></select><input type="text" name="'. self::$files_url .'[]" value="' . $addr . '"><span class="dashicons dashicons-trash remove-company-address"></span><span></span></span>';
    							unset($sword);
    							unset($sxls);
    							unset($sppt);
    							unset($spdf);
    							unset($szip);
    							unset($sjpg);
    							unset($savi);
    							unset($smp4);
    						$i++;
    						}
    					} else {
    						printf( $input, '' );
    					}
    
    					?>
    				</td>
    			</tr>
    		</table>
    
    		<?php
    	}
    
    	## Очищает и сохраняет значения полей
    	public function save_metabox( $post_id ) {
    
    		// Check if it's not an autosave.
    		if ( wp_is_post_autosave( $post_id ) )
    			return;
    
    		if ( isset( $_POST[self::$files_url] ) && is_array( $_POST[self::$files_url] ) ) {
    			$files_urls = $_POST[self::$files_url];
    
    			$files_urls = array_map( 'sanitize_text_field', $files_urls ); // очистка
    
    			$files_urls = array_filter( $files_urls ); // уберем пустые адреса
    
    			$files_type = $_POST[self::$files_type];
    
    			$files_type = array_filter( $files_type ); // уберем пустые поля "Выберите"
    
    			if ( $files_urls ) {
    				update_post_meta( $post_id, self::$files_type, $files_type );
    				update_post_meta( $post_id, self::$files_url, $files_urls );
    			} else {
    				delete_post_meta( $post_id, self::$files_type );
    				delete_post_meta( $post_id, self::$files_url );
    			}
    
    		}
    	}
    
    	## Подключает скрипты и стили
    	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
    	}
    
    }
    Ответить10.Апр.2019 в 13:18 #
  • Подскажите, пожалуйста, возможно ли каким либо плагином реализовать добавление групп полей в записи? Что бы можно было добавлять неограниченное количество а не только одну группу.

    Ответить24.Июн.2019 в 18:57 #
    • campusboy3504 www.youtube.com/c/wpplus

      Advanced Custom Fields, Carbon Fields, MetaBox и подобные. Они все позволяют несколько групп полей привязывать к сущности.

      1
      Ответить24.Июн.2019 в 20:24 #
      • Спасибо, сначала про Advanced Custom Fields и не думал, так как раньше еще видел в нем вывод только одной группы полей в записи. В про версии только увиде нужный как раз для моих целей тип "Повторитель" ))

        Ответить24.Июн.2019 в 21:46 #
      • Александр

        А как вывести например Repeater на странице, защищённой паролем ? Чтобы они были видны только после ввода пароля

        Ответить26.Июл.2019 в 20:00 #
  • @ Андрей

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

    Можно получить только записи с указанным шаблоном. Например, можно вывести все страницы с шаблоном «Услуги» (файл servises.php). Иногда это удобно. Название файла шаблона хранится в метаполе _wp_page_template, поэтому чтобы вывести страницы с указанным шаблоном нужно создать запрос по метаполю (см. WP_Query).

    Это можно применить в момент создания произвольного поля?

    Ответить11.Сен.2019 в 23:05 #
    • Kama7680

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

      function extra_fields_box_func( $post ){
      
      	if( 'servises.php' !== $post->_wp_page_template )
      		return;
      
      	// тут код метаблока
      }
      Ответить22.Мар.2020 в 16:38 #
  • kolshix598 paxtoy.com

    дополнение к сатье

    // Скрыть мета поля для постов в момент редактирования
    function my_exclude_custom_fields( $protected, $meta_key ) {
    	$hide_meta_array = array( 'post_desc', 'title_publisher' ) ; // сюда через запятую - список скрываемых полей 
    	if ( in_array( $meta_key, $hide_meta_array ) ) {
    	  return true;
    	}
    	return $protected;
    }
    add_filter( 'is_protected_meta', 'my_exclude_custom_fields', 10, 2 );
    Ответить11.Ноя.2019 в 16:16 #
  • Приветствую, возник вопрос:
    как при сохранении проверять поле типа select? Там же пустого значения, в принципе, не будет.
    В частности, это актуально, если создать повторяющиеся поля select. Тогда, если добавить поле, но не заполнить его и сохранить пост, в БД запишется дефолтное значение. Соответственно, при выводе инфы из данного поля в публичной части придется учитывать этот нюанс.
    Поэтому, хотелось бы понять, как фильтровать на этапе сохранения.

    Ответить30.Дек.2019 в 23:19 #
  • Максим

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

    Ответить03.Фев.2020 в 11:45 #
  • Здравствуйте. Проблема заключается в следующем. Использовал у себя в админке код для создания массива полей в метабоксах. Уже два дня убил, пытаясь заставить принимать записи одного типа разные значения. Результатов всегда несколько: либо при проверке через var_dump выясняется, что все записи принимают одно и тоже значение в метабоксе, либо вписанные значения не хотят сохраняться вообще, либо же я могу редактировать только одни метабоксы у одной конкретной записи, что вообще в данном случае делает наличие этих метабоксов бессмысленным. Что я только не делал. Пытался сделать массив с объектами, чтобы каждому отдельному объекту присваивать свой ID через метод save_metabox() (не работает). На чистом PHP обращался к $_POST, чтобы добыть ID из адреса страницы (не работает). Создавал global $post, и пытался передать в save_metabox($post->ID) (не работает). Обращался к базе данных, чтобы перебрать все значения ID и через цикл их передать в save_metabox() (не работает). Единственное, что работает -- это передача в save_metabox() числа напрямую, а именно -- ID записи. Очевидно, что никакой пользы метабоксы не несут, если их содержимое можно менять только у одной записи, а у других, как ни старайся, ничего не поменяется. Я вас очень прошу, пожалуйста, помогите мне и укажите на мои ошибки, ведь код рабочий, но, почему-то в моем случае, только для одной конкретной записи. Могу приложить примеры моих попыток решения, если это необходимо.

    Ответить09.Фев.2020 в 16:47 #
    • На данный момент удалось выяснить, что метод save_metabox() вызывается по какой-то причине дважды. Второй раз метод вызывается, когда я жму кнопку "Обновить", чтобы сохранить изменения в метабоксах, но результат никак не меняется.

      Ответить09.Фев.2020 в 18:03 #
    • код выложи

      Ответить09.Фев.2020 в 18:50 #
      • <?php
        
        class My_Best_Metaboxes {
        
        	public $post_type = 'post';
        
        	static $meta_key = 'company_address';
        
        	public function __construct() {
        		add_action( 'add_meta_boxes', array( $this, 'add_metabox' ) );
        		add_action( 'save_post_' . $this->post_type, array( $this, 'save_metabox' ) );
        		add_action( 'admin_print_footer_scripts', array( $this, 'show_assets' ), 10, 999 );
        		echo 'Первый метод ';
        	}
        
        	## Добавляет матабоксы
        	public function add_metabox() {
        		add_meta_box( 'box_info_company', 'Информация о компании', array( $this, 'render_metabox' ), $this->post_type, 'advanced', 'high' );
        		echo 'Второй метод ';
        	}
        
        	## Отображает метабокс на странице редактирования поста
        	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_key .'[]" value="%s">
        						<span class="dashicons dashicons-trash remove-company-address"></span>
        					</span>
        					';
        
        					$addresses = get_post_meta( $post->ID, self::$meta_key, true );
        
        					var_dump($post->ID);
        					echo 'Третий метод';
        
        					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 ) {
        
        		echo 'Четвертый метод, шаг первый ';
        
        		// Check if it's not an autosave.
        		if ( wp_is_post_autosave( $post_id ) )
        			return;
        
        		echo 'Четвертый метод, шаг второй ';
        
        		if ( isset( $_POST[self::$meta_key] ) && is_array( $_POST[self::$meta_key] ) ) {
        			$addresses = $_POST[self::$meta_key];
        
        			var_dump($addresses);
        
        			$addresses = array_map( 'sanitize_text_field', $addresses ); // очистка
        
        			$addresses = array_filter( $addresses ); // уберем пустые адреса
        
        			echo 'Четвертый метод, шаг третий ';
        
        			if ( $addresses ) {
        				if ($post_id != 0) {
        					update_post_meta( $post_id, self::$meta_key, $addresses );
        					echo 'Четвертый метод, шаг четвертый ';
        					echo $post_id;
        				}
        			}
        			else {
        				var_dump($addresses);
        				if ($post_id == 0) {
        					delete_post_meta( $post_id, self::$meta_key );
        					echo 'if в четвертом методе не сработал';
        					echo $post_id;
        				}
        			}
        
        		}
        	}
        
        	## Подключает скрипты и стили
        	public function show_assets() {
        		echo 'Пятый метод ';
        		if ( is_admin() && get_current_screen()->id == $this->post_type ) {
        			$this->show_styles();
        			$this->show_scripts();
        		}
        	}
        
        	## Выводит на экран стили
        	public function show_styles() {
        		echo 'Шестой метод '
        		?>
        		<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() {
        		echo 'Седьмой метод '
        		?>
        		<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
        	}
        
        }
        
        //Получаю ID текущей страницы.
        
        $path = $_SERVER['REQUEST_URI'];
        $currentPostID = stristr($path, '&', true);
        $currentPostID = preg_replace("/[^0-9]/", '', $currentPostID);
        
        //Инициализирую объект. Передаю в методы необходимые параметры.
        $met = new My_Best_Metaboxes;
        $met->post_type = 'alliance_properties';
        $met->save_metabox((int)$currentPostID);
        
        Ответить09.Фев.2020 в 20:14 #
        • Разрешил проблему, но вычурным способом. Полагаю, мой код мог быть и лучше в данном случае. Создал в БД отдельную маленькую таблицу, чтобы все работало. Код не самый красивый, но его еще предстоит "полировать".

          global $wpdb;
          
          $met = new My_Best_Metaboxes;
          $met->post_type = 'alliance_properties';
          
          $path = $_SERVER['REQUEST_URI'];
          $currentPostID = stristr($path, '&', true);
          $currentPostID = preg_replace("/[^0-9]/", '', $currentPostID);
          
          $currentPostID = (int)$currentPostID;
          
          $a="SELECT IsThatOldID FROM wp_temp";
          $x = $wpdb->get_results($a,ARRAY_A);
          foreach ($x as &$y) {
          	foreach ($y as &$value) {
          		$oldID = $value;
          	}
          }
          
          $oldID = (int)$oldID;
          
          if ( $currentPostID != 0) {
          	$wpdb->update( 'wp_temp',
          		array( 'IsThatOldID' => $currentPostID ),
          		array( 'IsThatOldID' => $oldID )
          	);
          }
          
          $a="SELECT IsThatOldID FROM wp_temp";
          $x = $wpdb->get_results($a,ARRAY_A);
          foreach ($x as &$y) {
          	foreach ($y as &$value) {
          		$oldID = $value;
          	}
          }
          
          $oldID = (int)$oldID;
          
          if ( $currentPostID == 0 ) {
          	$currentPostID = $oldID;
          } 
          
          if ( $oldID != $currentPostID ) {
          	if ( $oldID == 0 ) {
          		$oldID = $currentPostID;
          	}
          	$wpdb->update( 'wp_temp',
          		array( 'IsThatOldID' => $currentPostID ),
          		array( 'IsThatOldID' => $oldID )
          	);
          }
          
          $met->save_metabox((int)$currentPostID);
          Ответить10.Фев.2020 в 21:05 #
  • Андрей

    Здравствуйте! Помогите реализовать перевод кастомных полей в этой статье с помощью плагина WP Multilang, прикрепляю скриншоты что-бы вы увидели как реализовано вывод полей у меня в шаблоне. Как сделать так, что-бы для каждой версии языка в посте, сохранять и потом выводить нужные поля?

    Ответить05.Апр.2020 в 18:36 #