WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru

Kama_Post_Meta_Box — создаем метаполя для записей

В этой статье я поделись классом Kama Post Meta Box, с помощью которого можно быстро создавать метаполя для записей, просто указав их в виде массива. Получается своего рода конструктор. К тому же, этот вариант автоматически очищает данные при сохранении и в некоторых случаях может защитить от взлома сайта.

Статью на эту тему я уже писал: «Блок произвольных полей в админке WordPress своими руками». Правда было это давно, но статья по-прежнему актуальна и может пригодится, когда нужно создать произвольные поля для записи, без использования плагинов. Однако в том варианте все нужно делать вручную, включая создание html каждого поля формы — это не удобно. Также, тот вариант требует определенных знаний: умение работать с хуками и т.д.

Покажу на примере, как легко можно создать произвольные поля для записей. Допустим, нам нужно создать 4 SEO поля: title, description, keywords и robots для всех типов записей. Для создания метабокса нужно вызвать класс Kama_Post_Meta_Box с передачей ему параметров:

class_exists('Kama_Post_Meta_Box') && new Kama_Post_Meta_Box( array(
	'id'     => '_seo',
	'title'  => 'SEO поля',
	'fields' => array(
		'title' => array(
			'type'=>'text',    'title'=>'Title',       'desc'=>'Заголовок страницы (рекомендуется 70 символов)', 'attr'=>'style="width:99%;"'
		),
		'description' => array(
			'type'=>'textarea','title'=>'Description', 'desc'=>'Описание страницы (рекомендуется 160 символов)', 'attr'=>'style="width:99%;"'
		),
		'keywords' => array(
			'type'=>'text',    'title'=>'Keywords',    'desc'=>'Ключевые слова для записи',       'attr'=>'style="width:99%;"'
		),
		'robots' => array(
			'type'=>'radio',   'title'=>'Robots',      'options' => array(''=>'index,follow', 'noindex,nofollow'=>'noindex,nofollow')
		),
	),
) );

В результате в админке на страницах редактирования записей любого типа (post, page, ...) получим такой метабокс:

Создание произвольных полей для записи

Удобно? По-моему очень!

Чтобы такой код работал нужно подключить в functions.php (или куда-то еще) следующий код (PHP класс)

меню

Класс: Kama_Post_Meta_Box

GitHub
<?php

if( ! class_exists('Kama_Post_Meta_Box') ) :

	/**
	 * Создает блок произвольных полей для указанных типов записей.
	 *
	 * Возможные параметры класса, смотрите в: Kama_Post_Meta_Box::__construct()
	 * Возможные параметры для каждого поля, смотрите в: Kama_Post_Meta_Box::field()
	 *
	 * При сохранении, очищает каждое поле, через: wp_kses() или sanitize_text_field().
	 * Функцию очистки можно заменить через хук 'kpmb_save_sanitize_{$id}' и
	 * также можно указать название функции очистки в параметре 'save_sanitize'.
	 * Если указать функции очистки и в параметре, и в хуке, то будут работать обе!
	 * Обе функции очистки получают данные: $metas - все метаполя, $post_id - ID записи.
	 *
	 * Блок выводиться и метаполя сохраняются для юзеров с правом редактировать текущую запись.
	 *
	 * PHP: 5.3+
	 *
	 * @changlog https://github.com/doiftrue/Kama_Post_Meta_Box/blob/master/changelog.md
	 *
	 * @version 1.9.7
	 */
	class Kama_Post_Meta_Box {

		public $opt;

		public $id;

		static $instances = array(); // сохраняет ссылки на все экземпляры

		/**
		 * Конструктор
		 *
		 * @param array $opt Опции по которым будет строиться метаблок
		 */
		function __construct( $opt ){

			$defaults = [
				'id'         => '',       // динетификатор блока. Используется как префикс для названия метаполя.
				// начните идент. с '_': '_foo', чтобы ID не был префиксом в названии метаполей.

				'title'      => '',       // заголовок блока
				'desc'       => '',       // описание для метабокса. Можно указать функцию/замыкание, она получит $post. С версии 1.9.1
				'post_type'  => '',       // строка/массив. Типы записей для которых добавляется блок: array('post','page').
				// По умолчанию: '' - для всех типов записей.
				'post_type_feature' => '', // строка. возможность которая должна быть у типа записи,
				// чтобы метабокс отобразился. См. post_type_supports()
				'post_type_options' => '', // массив. опции типа записи, которые должны быть у типа записи чтобы метабокс отобразился.
				// см. первый параметр get_post_types()

				'priority'   => 'high',   // Приоритет блока для показа выше или ниже остальных блоков ('high' или 'low').
				'context'    => 'normal', // Место где должен показываться блок ('normal', 'advanced' или 'side').

				'disable_func'  => '',    // функция отключения метабокса во время вызова самого метабокса.
				// Если вернет что-либо кроме false/null/0/array(), то метабокс будет отключен. Передает объект поста.

				'cap'           => '',    // название права пользователя, чтобы показывать метабокс.

				'save_sanitize' => '',    // Функция очистки сохраняемых в БД полей. Получает 2 параметра:
				// $metas - все поля для очистки и $post_id

				'theme' => 'table',       // тема оформления: 'table', 'line'.
				// ИЛИ массив паттернов полей: css, fields_wrap, field_wrap, title_patt, field_patt, desc_patt.
				// Массив указывается так: [ 'desc_patt' => '<div>%s</div>' ] (за овнову будет взята тема line)
				// Массив указывается так: [ 'table' => [ 'desc_patt' => '<div>%s</div>' ] ] (за овнову будет взята тема table)
				// ИЛИ изменить тему можно через фильтр 'kp_metabox_theme' - удобен для общего изменения темы для всех метабоксов.

				// метаполя. Параметры смотрите ниже в методе field()
				'fields'     => [
					'foo' => [ 'title' =>'Первое метаполе' ],
					'bar' => [ 'title' =>'Второе метаполе' ],
				],
			];

			$this->opt = (object) array_merge( $defaults, $opt );

			// хуки инициализации, вешается на хук init чтобы текущий пользователь уже был установлен
			add_action( 'init', [ $this, 'init_hooks' ], 20 );
		}

		function init_hooks(){
			// может метабокс отключен по праву
			if( $this->opt->cap && ! current_user_can( $this->opt->cap ) )
				return;

			// темы оформления
			if( 'theme_options' ){

				$opt_theme = & $this->opt->theme;

				$def_opt_theme = [
					'line' => [
						// CSS стили всего блока. Например: '.postbox .tit{ font-weight:bold; }'
						'css'         => '',
						// '%s' будет заменено на html всех полей
						'fields_wrap' => '%s',
						// '%2$s' будет заменено на html поля (вместе с заголовком, полем и описанием)
						'field_wrap'  => '<p class="%1$s">%2$s</p>',
						// '%s' будет заменено на заголовок
						'title_patt'  => '<strong class="tit"><label>%s</label></strong>',
						// '%s' будет заменено на HTML поля (вместе с описанием)
						'field_patt'  => '%s',
						// '%s' будет заменено на текст описания
						'desc_patt'   => '<br><span class="description" style="opacity:0.6;">%s</span>',
					],
					'table' => [
						'css'         => '.kpmb-table td{ padding:.6em .5em; } .kpmb-table tr:hover{ background:rgba(0,0,0,.03); }',
						'fields_wrap' => '<table class="form-table kpmb-table">%s</table>',
						'field_wrap'  => '<tr class="%1$s">%2$s</tr>',
						'title_patt'  => '<td style="width:10em;" class="tit">%s</td>',
						'field_patt'  => '<td class="field">%s</td>',
						'desc_patt'   => '<br><span class="description" style="opacity:0.8;">%s</span>',
					],
				];

				if( is_string($opt_theme) ){
					$def_opt_theme = $def_opt_theme[ $opt_theme ];
				}
				// позволяет изменить отдельные поля темы оформелния метабокса
				else {
					$opt_theme_key = key( $opt_theme ); // индекс массива

					// в индексе указана не тема: [ 'desc_patt' => '<div>%s</div>' ]
					if( ! in_array( $opt_theme_key, array_keys($def_opt_theme) ) ){
						$def_opt_theme = $def_opt_theme['line']; // основа темы
					}
					// в индексе указана тема: [ 'table' => [ 'desc_patt' => '<div>%s</div>' ] ]
					else {
						$def_opt_theme = $def_opt_theme[ $opt_theme_key ]; // основа темы
						$opt_theme     = $opt_theme[ $opt_theme_key ];
					}
				}

				$opt_theme = is_array( $opt_theme ) ? array_merge( $def_opt_theme, $opt_theme ) : $def_opt_theme;

				// для изменения темы через фильтр
				$opt_theme = apply_filters( 'kp_metabox_theme', $opt_theme, $this->opt );

				// переменные theme в общие параметры.
				// Если в параметрах уже есть переменная, то она остается как есть (это позволяет изменить отдельный элемент темы).
				foreach( $opt_theme as $kk => $vv ){
					if( ! isset($this->opt->{$kk}) )
						$this->opt->{$kk} = $vv;
				}
			}

			// создадим уникальный ID объекта
			$_opt = (array) clone $this->opt;
			// удалим (очистим) все closure
			array_walk_recursive( $_opt, function(&$val, $key){
				if( $val instanceof Closure ) $val = '';
			});
			$this->id = substr( md5(serialize($_opt)), 0, 7 ); // ID экземпляра

			// сохраним ссылку на экземпляр, чтобы к нему был доступ
			self::$instances[ $this->opt->id ][ $this->id ] = & $this;

			add_action( 'add_meta_boxes', [ $this, 'add_meta_box' ], 10, 2 );
			add_action( 'save_post', [ $this, 'meta_box_save' ], 1, 2 );
		}

		function add_meta_box( $post_type, $post ){

			$opt = $this->opt; // short love

			// может отключить метабокс?
			if(
				in_array( $post_type, [ 'comment','link' ] )
				|| ! current_user_can( get_post_type_object( $post_type )->cap->edit_post, $post->ID )
				|| ( $opt->post_type_feature && ! post_type_supports( $post_type, $opt->post_type_feature ) )
				|| ( $opt->post_type_options && ! in_array( $post_type, get_post_types( $opt->post_type_options, 'names', 'or' ) ) )
				|| ( $opt->disable_func && is_callable($opt->disable_func) && call_user_func( $opt->disable_func, $post ) )
			)
				return;

			$p_types = $opt->post_type ?: $post_type;

			// if WP < 4.4
			if( is_array($p_types) && version_compare( $GLOBALS['wp_version'], '4.4', '<' ) ){
				foreach( $p_types as $p_type )
					add_meta_box( $this->id, $opt->title, [ $this, 'meta_box' ], $p_type, $opt->context, $opt->priority );
			}
			else
				add_meta_box( $this->id, $opt->title, [ $this, 'meta_box' ], $p_types, $opt->context, $opt->priority );

			// добавим css класс к метабоксу
			// apply_filters( "postbox_classes_{$page}_{$id}", $classes );
			add_filter( "postbox_classes_{$post_type}_{$this->id}", [ $this, '_postbox_classes_add' ] );
		}

		/**
		 * Выводит код блока
		 * @param object $post Объект записи
		 */
		function meta_box( $post ){
			$fields_out = $hidden_out = '';

			foreach( $this->opt->fields as $key => $args ){
				if( ! $key || ! $args ) continue; // пустое поле

				if( empty($args['title_patt']) ) $args['title_patt'] = @ $this->opt->title_patt ?: '%s';
				if( empty($args['desc_patt'])  ) $args['desc_patt']  = @ $this->opt->desc_patt  ?: '%s';
				if( empty($args['field_patt']) ) $args['field_patt'] = @ $this->opt->field_patt ?: '%s';

				$args['key'] = $key;

				$field_wrap = & $this->opt->field_wrap;
				if( @ $args['type'] == 'wp_editor' )  $field_wrap = str_replace( [ '<p ','</p>' ], [ '<div ','</div><br>' ], $field_wrap );

				if( @ $args['type'] == 'hidden' )
					$hidden_out .= $this->field( $args, $post );
				else
					$fields_out .= sprintf( $field_wrap, $key .'_meta', $this->field( $args, $post ) );

			}

			$metabox_desc = '';
			if( $this->opt->desc )
				$metabox_desc = is_callable($this->opt->desc) ? call_user_func($this->opt->desc, $post) : '<p class="description">'. $this->opt->desc .'</p>';

			echo ( $this->opt->css ? '<style>'. $this->opt->css .'</style>' : '' ) .
			     $metabox_desc .
			     $hidden_out .
			     sprintf( (@ $this->opt->fields_wrap ?: '%s'), $fields_out ) .
			     '<div class="clear"></div>';
		}

		/**
		 * Выводит отдельные мета поля.
		 *
		 * @param string  $name  Атрибут name.
		 * @param array   $args  Параметры поля.
		 * @param object  $post  Объект текущего поста.
		 *
		 * @return string|null HTML код
		 */
		function field( $args, $post ){

			$var = (object) []; // внутренние переменные этой фукнции, будут переданы в методы
			$rg  = (object) array_merge( [
				'type'          => '', // тип поля: textarea, select, checkbox, radio, image, wp_editor, hidden, sep_*.
				// Или базовые: text, email, number, url, tel, color, password, date, month, week, range.
				// 'sep' - визуальный разделитель, для него нужно указать `title` и можно указать `'attr'=>'style="свои стили"'`.
				// 'sep' - чтобы удобнее указывать тип 'sep' начните ключ поля с `sep_`: 'sep_1' => [ 'title'=>'Разделитель' ].
				// Для типа `image` можно указать тип сохраняемого значения в `options`: 'options'=>'url'. По умолчанию тип = id.
				// По умолчанию 'text'.

				'title'         => '', // заголовок метаполя
				'desc'          => '', // описание для поля. Можно указать функцию/замыкание, она получит параметры: $post, $meta_key, $val, $name.
				'placeholder'   => '', // атрибут placeholder
				'id'            => '', // атрибут id. По умолчанию: $this->opt->id .'_'. $key
				'class'         => '', // атрибут class: добавляется в input, textarea, select. Для checkbox, radio в оборачивающий label
				'attr'          => '', // любая строка, будет расположена внутри тега. Для создания атрибутов. Пр: style="width:100%;"
				'val'           => '', // значение по умолчанию, если нет сохраненного.
				'options'       => '', // массив: array('значение'=>'название') - варианты для типов 'select', 'radio'.
				// Для 'wp_editor' стенет аргументами.
				// Для 'checkbox' станет значением атрибута value: <input type="checkbox" value="{options}">.
				// Для 'image' определяет тип сохраняемого в метаполе значения: id (ID вложения), url (url вложения).

				'callback'      => '', // название функции, которая отвечает за вывод поля.
				// если указана, то ни один параметр не учитывается и за вывод полностью отвечает указанная функция.
				// Все параметры передаются ей... Получит параметры: $args, $post, $name, $val

				'sanitize_func' => '', // функция очистки данных при сохранении - название функции или Closure.
				// Укажите 'none', чтобы не очищать данные...
				// работает, только если не установлен глобальный параметр 'save_sanitize'...
				// получит параметр $value - сохраняемое значение поля.

				'output_func'   => '', // функция обработки значения, перед выводом в поле.
				// получит параметры: $post, $meta_key, $value - объект записи, ключ, значение метаполей.

				'update_func'   => '', // функция сохранения значения в метаполя.
				// получит параметры: $post, $meta_key, $value - объект записи, ключ, значение метаполей.

				'disable_func'  => '', // функция отключения поля.
				// Если не false/null/0/array() - что-либо вернет, то поле не будет выведено.
				// Получает парамтры: $post, $meta_key

				'cap'           => '', // название права пользователя, чтобы видеть и изменять поле.

				// служебные
				'key'           => '', // Обязательный! Автоматический
				'title_patt'    => '', // Обязательный! Автоматический
				'field_patt'    => '', // Обязательный! Автоматический
				'desc_patt'     => '', // Обязательный! Автоматический
			], $args );

			if( $rg->cap && ! current_user_can( $rg->cap ) )
				return null;

			if( 'sep_' === substr($rg->key, 0, 4) ) $rg->type = 'sep';
			if( ! $rg->type )                       $rg->type = 'text';

			$var->meta_key = $this->_key_prefix() . $rg->key;

			// поле отключено
			if( $rg->disable_func && is_callable($rg->disable_func) && call_user_func( $rg->disable_func, $post, $var->meta_key ) )
				return null;

			// meta_val
			$rg->val = get_post_meta( $post->ID, $var->meta_key, true ) ?: $rg->val;
			if( $rg->output_func && is_callable($rg->output_func) )
				$rg->val = call_user_func( $rg->output_func, $post, $var->meta_key, $rg->val );

			$var->name = $this->id . "_meta[$var->meta_key]";

			$rg->id  = $rg->id ?: ( $this->opt->id .'_'. $rg->key );

			// при табличной теме, td заголовка должен выводиться всегда!
			if( false !== strpos($rg->title_patt, '<td ') )
				$var->title = sprintf( $rg->title_patt, $rg->title ) . ($rg->title ? ' ' : '');
			else
				$var->title = $rg->title ? sprintf( $rg->title_patt, $rg->title ) .' ' : '';

			$rg->options = (array) $rg->options;

			$var->pholder = $rg->placeholder ? ' placeholder="'. esc_attr($rg->placeholder) .'"' : '';
			$var->class = $rg->class ? ' class="'. $rg->class .'"' : '';

			$fn__desc = function() use ( $rg, $post, $var ){
				if( ! $rg->desc ) return '';
				$desc = is_callable( $rg->desc ) ? call_user_func_array($rg->desc, [ $post, $var->meta_key, $rg->val, $var->name ] ) : $rg->desc;
				return sprintf( $rg->desc_patt, $desc );
			};

			$fn__field = function( $field ) use ( $rg ){
				return sprintf( $rg->field_patt, $field );
			};

			// произвольная функция
			if( is_callable( $rg->callback ) )
				$out = $var->title . $fn__field( call_user_func_array( $rg->callback, [ $args, $post, $var->name, $rg->val, $rg, $var ] ) );
			// произвольный метод
			// вызов метода `$this->field__{FIELD}()` (для возможности расширить этот класс)
			elseif( method_exists( $this, "field__$rg->type") )
				$out = $this->{"field__$rg->type"}( $rg, $var, $post, $fn__desc, $fn__field );
			// text, email, number, url, tel, color, password, date, month, week, range
			else
				$out = $this->field__default( $rg, $var, $post, $fn__desc, $fn__field );

			return $out;
		}

		// textarea
		function field__textarea( $rg, $var, $post, $fn__desc, $fn__field ){
			$_style = (false === strpos($rg->attr,'style=')) ? ' style="width:98%;"' : '';

			return $var->title . $fn__field('<textarea '. $rg->attr . $var->class . $var->pholder . $_style .'  id="'. $rg->id .'" name="'. $var->name .'">'. esc_textarea($rg->val) .'</textarea>'. $fn__desc() );
		}

		// select
		function field__select( $rg, $var, $post, $fn__desc, $fn__field ){
			$is_assoc = ( array_keys($rg->options) !== range(0, count($rg->options) - 1) ); // ассоциативный или нет?
			$_options = array();
			foreach( $rg->options as $v => $l ){
				$_val       = $is_assoc ? $v : $l;
				$_options[] = '<option value="'. esc_attr($_val) .'" '. selected($rg->val, $_val, 0) .'>'. $l .'</option>';
			}

			return $var->title . $fn__field('<select '. $rg->attr . $var->class .' id="'. $rg->id .'" name="'. $var->name .'">' . implode("\n", $_options ) . '</select>' . $fn__desc() );
		}

		// radio
		function field__radio( $rg, $var, $post, $fn__desc, $fn__field ){
			$radios = array();
			foreach( $rg->options as $v => $l )
				$radios[] = '<label '. $rg->attr . $var->class .'><input type="radio" name="'. $var->name .'" value="'. $v .'" '. checked($rg->val, $v, 0) .'>'. $l .'</label> ';

			return $var->title . $fn__field('<span class="radios">'. implode("\n", $radios ) .'</span>'. $fn__desc() );
		}

		// checkbox
		function field__checkbox( $rg, $var, $post, $fn__desc, $fn__field ){
			return $var->title . $fn__field('
				<label '. $rg->attr . $var->class .'>
					<input type="hidden" name="'. $var->name .'" value="">
					<input type="checkbox" id="'. $rg->id .'" name="'. $var->name .'" value="'. esc_attr(reset($rg->options) ?: 1) .'" '. checked( $rg->val, (reset($rg->options) ?: 1), 0) .'>
					'.( $rg->desc ?: '' ).'
				</label>');
		}

		// sep
		function field__sep( $rg, $var, $post, $fn__desc, $fn__field ){
			$_style = 'font-weight:600; ';
			if( preg_match( '/style="([^"]+)"/', $rg->attr, $mm ) ) $_style .= $mm[1];

			if( false !== strpos( $rg->field_patt, '<td' ) )
				return str_replace( '<td ', '<td colspan="2" style="padding:1em .5em; '. $_style .'"', $fn__field( $rg->title ) );
			else
				return '<span style="display:block; padding:1em 0; font-size:110%; '. $_style .'">'. $rg->title .'</span>';
		}

		// hidden
		function field__hidden( $rg, $var, $post, $fn__desc, $fn__field ){
			return '<input type="'. $rg->type .'" id="'. $rg->id .'" name="'. $var->name .'" value="'. esc_attr($rg->val) .'" title="'. esc_attr($rg->title) .'">';
		}

		// text, email, number, url, tel, color, password, date, month, week, range
		function field__default( $rg, $var, $post, $fn__desc, $fn__field ){
			$_style   = ( $rg->type === 'text' && false === strpos($rg->attr, 'style=') ) ? ' style="width:100%;"' : '';

			return $var->title . $fn__field( '<input '. $rg->attr . $var->class  . $var->pholder . $_style .' type="'. $rg->type .'" id="'. $rg->id .'" name="'. $var->name .'" value="'. esc_attr($rg->val) .'">'. $fn__desc() );
		}

		// wp_editor
		function field__wp_editor( $rg, $var, $post, $fn__desc, $fn__field ){
			$ed_args = array_merge( [
				'textarea_name'    => $var->name, //нужно указывать!
				'editor_class'     => $rg->class,
				// изменяемое
				'wpautop'          => 1,
				'textarea_rows'    => 5,
				'tabindex'         => null,
				'editor_css'       => '',
				'teeny'            => 0,
				'dfw'              => 0,
				'tinymce'          => 1,
				'quicktags'        => 1,
				'media_buttons'    => false,
				'drag_drop_upload' => false,
			], $rg->options );

			ob_start();
			wp_editor( $rg->val, $rg->id, $ed_args );
			$wp_editor = ob_get_clean();

			return $var->title . $fn__field( $wp_editor . $fn__desc() );
		}

		// image
		function field__image( $rg, $var, $post, $fn__desc, $fn__field ){

			wp_enqueue_media();

			static $once;
			if( ! $once && $once = 1 ){
				add_action( 'admin_print_footer_scripts', function(){
					?>
					<script>
					$('.kmb_img_wrap').each(function(){

						var frame,
							$wrap = $(this),
							$img   = $wrap.find('img'),
							$input = $wrap.find('input[type="hidden"]');

						$wrap.on( 'click', '.set_img', function(){

							var post_id = $(this).data('post_id') || null

							//if( frame && frame.post_id === post_id ){
							//	frame.open();
							//	return;
							//}

							frame = wp.media.frames.kmbframe = wp.media({
								title   : '<?= __( 'Add Media' ) ?>',
								// Library WordPress query arguments.
								library : {
									type       : 'image',
									uploadedTo : post_id
								},
								multiple: false,
								button: {
									text: '<?= __( 'Apply' ) ?>'
								}
							});

							frame.on( 'select', function() {
								attachment = frame.state().get('selection').first().toJSON();
								$img.attr( 'src', attachment.url );

								$wrap.data('usetype') === 'url' ? $input.val( attachment.url ) : $input.val( attachment.id );
							});

							frame.on( 'open', function(){
								if( $input.val() )
									frame.state().get('selection').add( wp.media.attachment( $input.val() ) );
							});

							frame.open();
							//frame.post_id = post_id // save
						});

						$wrap.on( 'click', '.del_img', function(){
							$img.attr( 'src', '' );
							$input.val('');
						});
					})
					</script>
					<?php
				}, 99 );
			}

			$usetype = $rg->options ? $rg->options[0] : 'id'; // может быть: id, url

			if( ! $src = is_numeric($rg->val) ? wp_get_attachment_url( $rg->val ) : $rg->val )
				$src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';

			ob_start();
			?>
			<span class="kmb_img_wrap" data-usetype="<?= esc_attr($usetype) ?>" style="display:flex; align-items:center;">
				<img src="<?= esc_url($src) ?>" style="max-height:100px; max-width:100px; margin-right:1em;" alt="">
				<span>
					<input class="set_img button button-small" type="button" value="<?= __('Set image') ?>" />
					<input class="set_img button button-small" type="button" data-post_id="<?= $post->ID ?>" value="<?= __( 'Uploaded to this post' ) ?>" />
					<input class="del_img button button-small" type="button" value="<?= __('Remove')?>" />

					<input type="hidden" name="<?= $var->name ?>" value="<?= esc_attr($rg->val) ?>">
				</span>
			</span>
			<?php
			$field = ob_get_clean();

			return $var->title . $fn__field( $field );
		}

		/**
		 * Сохраняем данные, при сохранении поста
		 * @param  integer $post_id ID записи
		 * @return boolean  false если проверка не пройдена
		 */
		function meta_box_save( $post_id, $post ){

			if(	! ( $save_metadata = @ $_POST["{$this->id}_meta"] )                                        // нет данных
			       || ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE  )                                           // выходим, если это автосохр.
			       || ! wp_verify_nonce( $_POST['_wpnonce'], 'update-post_'. $post_id )                          // nonce проверка
			       || ( $this->opt->post_type && ! in_array( $post->post_type, (array) $this->opt->post_type ) ) // не подходящий тип записи
			)
				return null;

			// оставим только поля текущего класса (защиты от подмены поля)
			$_key_prefix = $this->_key_prefix();
			$fields_data = array();
			foreach( $this->opt->fields as $_key => $rg ){
				$meta_key = $_key_prefix . $_key;

				// недостаточно прав
				if( !empty($rg['cap']) && ! current_user_can( $rg['cap'] ) )
					continue;

				// пропускаем отключенные поля
				if( !empty($rg['disable_func']) && is_callable($rg['disable_func']) && call_user_func( $rg['disable_func'], $post, $meta_key ) )
					continue;

				$fields_data[ $meta_key ] = $rg;
			}
			$fields_names  = array_keys( $fields_data );
			$save_metadata = array_intersect_key( $save_metadata, array_flip($fields_names) );


			// Очистка
			if( 'sanitize' ){
				// своя функция очистки
				if( is_callable($this->opt->save_sanitize) ){
					$save_metadata = call_user_func_array( $this->opt->save_sanitize, [ $save_metadata, $post_id, $fields_data ] );
					$sanitized = true;
				}
				// хук очистки
				if( has_filter("kpmb_save_sanitize_{$this->opt->id}") ){
					$save_metadata = apply_filters("kpmb_save_sanitize_{$this->opt->id}", $save_metadata, $post_id, $fields_data );
					$sanitized = true;
				}
				// если нет функции и хука очистки, то чистим все поля с помощью wp_kses() или sanitize_text_field()
				if( empty($sanitized) ){

					foreach( $save_metadata as $meta_key => & $value ){
						// есть функция очистки отдельного поля
						if( !empty($fields_data[$meta_key]['sanitize_func']) && is_callable($fields_data[$meta_key]['sanitize_func']) ){
							$value = call_user_func( $fields_data[$meta_key]['sanitize_func'], $value );
						}
						// не чистим
						elseif( @ $fields_data[$meta_key]['sanitize_func'] === 'none' ){}
						// не чистим - видимо это произвольная функция вывода полей, которая сохраняет массив
						elseif( is_array($value) ){}
						// нет функции очистки отдельного поля
						else {

							$type = !empty($fields_data[$meta_key]['type']) ? $fields_data[$meta_key]['type'] : 'text';

							if(0){}
							elseif( $type === 'number' )
								$value = floatval( $value );
							elseif( $type === 'email' )
								$value = sanitize_email( $value );
							// wp_editor, textarea
							elseif( in_array( $type, [ 'wp_editor','textarea' ], true ) )
								$value = addslashes( wp_kses( stripslashes( $value ), 'post' ) ); // default ?
							// text, radio, checkbox, color, date, month, tel, time, url
							else
								$value = sanitize_text_field( $value );
						}
					}
					unset($value); // $value используется ниже, поэтому он должен быть пустой, а не ссылкой...

				}
			}

			// Сохраняем
			foreach( $save_metadata as $meta_key => $value ){
				// если есть функция сохранения
				if( !empty($fields_data[$meta_key]['update_func']) && is_callable($fields_data[$meta_key]['update_func']) ){
					call_user_func( $fields_data[$meta_key]['update_func'], $post, $meta_key, $value );
				}
				else {
					// удаляем поле, если значение пустое. 0 остается...
					if( ! $value && ($value !== '0') )
						delete_post_meta( $post_id, $meta_key );
					else
						update_post_meta( $post_id, $meta_key, $value ); // add_post_meta() работает автоматически
				}
			}
		}

		function _postbox_classes_add( $classes ){
			$classes[] = "kama_meta_box_{$this->opt->id}";
			return $classes;
		}

		function _key_prefix(){
			return ($this->opt->id{0} == '_') ? '' : $this->opt->id .'_';
		}

	}

endif;


меню

Передаваемые параметры

Прежде чем переходить к остальным примерам, рассмотрим все параметры, которые понимает класс:

new Kama_Post_Meta_Box( array(

	'id'         => '',       // динетификатор блока. Используется как префикс для названия метаполя.
	// начните идент. с '_': '_foo', чтобы ID не был префиксом в названии метаполей.

	'title'      => '',       // заголовок блока
	'desc'       => '',       // описание для метабокса. Можно указать функцию/замыкание, она получит $post. С версии 1.9.1
	'post_type'  => '',       // строка/массив. Типы записей для которых добавляется блок: array('post','page').
	// По умолчанию: '' - для всех типов записей.
	'post_type_feature' => '', // строка. возможность которая должна быть у типа записи,
	// чтобы метабокс отобразился. См. post_type_supports()
	'post_type_options' => '', // массив. опции типа записи, которые должны быть у типа записи чтобы метабокс отобразился.
	// см. первый параметр get_post_types()

	'priority'   => 'high',   // Приоритет блока для показа выше или ниже остальных блоков ('high' или 'low').
	'context'    => 'normal', // Место где должен показываться блок ('normal', 'advanced' или 'side').

	'disable_func'  => '',    // функция отключения метабокса во время вызова самого метабокса.
	// Если вернет что-либо кроме false/null/0/array(), то метабокс будет отключен. Передает объект поста.

	'cap'           => '',    // название права пользователя, чтобы показывать метабокс.

	'save_sanitize' => '',    // Функция очистки сохраняемых в БД полей. Получает 2 параметра: $metas - все поля для очистки и $post_id

	'theme' => 'table',       // тема оформления: 'table', 'line'.
	// ИЛИ массив паттернов полей: css, fields_wrap, field_wrap, title_patt, field_patt, desc_patt.
	// Массив указывается так: [ 'desc_patt' => '<div>%s</div>' ] (за овнову будет взята тема line)
	// Массив указывается так: [ 'table' => [ 'desc_patt' => '<div>%s</div>' ] ] (за овнову будет взята тема table)
	// ИЛИ изменить тему можно через фильтр 'kp_metabox_theme' - удобен для общего изменения темы для всех метабоксов.

	// Массив метаполей, которые будут выводиться
	'fields'     => array(

		// Каждое поле указывается в виде массива, где ключ - это название метаполя, а значение - это массив данных о поле
		// реальное название метаполя будет выглядеть как: {id}_{meta_key}
		'meta_key' => array(

			'type'          => '', // тип поля: textarea, select, checkbox, radio, image, wp_editor, hidden, sep_*.
			// Или базовые: text, email, number, url, tel, color, password, date, month, week, range.
			// 'sep' - визуальный разделитель, для него нужно указать `title` и можно указать `'attr'=>'style="свои стили"'`.
			// 'sep' - чтобы удобнее указывать тип 'sep' начните ключ поля с `sep_`: 'sep_1' => [ 'title'=>'Разделитель' ].
			// Для типа `image` можно указать тип сохраняемого значения в `options`: 'options'=>'url'. По умолчанию тип = id.
			// По умолчанию 'text'.

			'title'         => '', // заголовок метаполя
			'desc'          => '', // описание для поля. Можно указать функцию/замыкание, она получит параметры: $post, $meta_key, $val, $name.
			'placeholder'   => '', // атрибут placeholder
			'id'            => '', // атрибут id. По умолчанию: $this->opt->id .'_'. $key
			'class'         => '', // атрибут class: добавляется в input, textarea, select. Для checkbox, radio в оборачивающий label
			'attr'          => '', // любая строка, будет расположена внутри тега. Для создания атрибутов. Пр: style="width:100%;"
			'val'           => '', // значение по умолчанию, если нет сохраненного.
			'options'       => '', // массив: array('значение'=>'название') - варианты для типов 'select', 'radio'.
			// Для 'wp_editor' стенет аргументами.
			// Для 'checkbox' станет значением атрибута value: <input type="checkbox" value="{options}">.
			// Для 'image' определяет тип сохраняемого в метаполе значения: id (ID вложения), url (url вложения).

			'callback'      => '', // название функции, которая отвечает за вывод поля.
			// если указана, то ни один параметр не учитывается и за вывод полностью отвечает указанная функция.
			// Все параметры передаются ей... Получит параметры: $args, $post, $name, $val

			'sanitize_func' => '', // функция очистки данных при сохранении - название функции или Closure.
			// Укажите 'none', чтобы не очищать данные...
			// работает, только если не установлен глобальный параметр 'save_sanitize'...
			// получит параметр $value - сохраняемое значение поля.

			'output_func'   => '', // функция обработки значения, перед выводом в поле.
			// получит параметры: $post, $meta_key, $value - объект записи, ключ, значение метаполей.

			'update_func'   => '', // функция сохранения значения в метаполя.
			// получит параметры: $post, $meta_key, $value - объект записи, ключ, значение метаполей.

			'disable_func'  => '', // функция отключения поля.
			// Если не false/null/0/array() - что-либо вернет, то поле не будет выведено.
			// Получает парамтры: $post, $meta_key

			'cap'           => '', // название права пользователя, чтобы видеть и изменять поле.

		),
		'meta_key2' => array( /*...*/ ),
		'meta_key3' => array( /*...*/ ),
		// ...
	),
) );
меню

Заметки

Названия произвольных полей

Создаваемые произвольные поля будут иметь название/ключ, состоящий из объединения основного ID и указанного ключа метаполя: {id}_{meta_key} (смотрите пример ниже).

Самый просто способ узнать название произвольного поля — это посмотреть в исходный код. Для этого фокусируемся на нужном поле, кликаем правой кнопкой и смотрим значение атрибута name в исходном коде элемента:

Как узнать название метаполя

Получение произвольных полей

Получать созданные поля для использования в темах и плагинах нужно стандартной функцией WordPress: get_post_meta():

// получим значение поля 'my_meta_key' у записи 25
$my_filed = get_post_meta( 25, 'my_meta_key', 1 );
echo $my_filed;

Права

  • Блок/метабокс будет показан только пользователям, у которых есть право редактировать текущую запись.

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

Примеры создания различных произвольных полей

#1 Демонстрация создания всех видов метаполей

Этот пример показывает как создавать все поддерживаемые типы метаполей: 'text', 'textarea', 'select', 'checkbox', 'radio', 'wp_editor', 'hidden' и другие: 'email', 'number', 'phone', 'password' и т.д. (обрабатываются как поле 'text').

class_exists('Kama_Post_Meta_Box') && new Kama_Post_Meta_Box(
	array(
		'id'         => 'my',
		'title'      => 'Мои произвольные поля',
		'fields'     => array(
			'text_field'      => array(
				'title' => 'Текстовое поле'
			),
			'number_field'    => array(
				'type'=>'number', 'title'=>'Числовое поле', 'desc'=>'Число от 0 до 5', 'attr'=>'min="0" max="5"'
			),
			'textarea_field'  => array(
				'type'=>'textarea', 'title'=>'Большое текстовое поле', 'desc'=>'Описание чего-либо. Можно использовать html теги.'
			),
			'select_field'    => array(
				'type'=>'select', 'title'=>'Выберите значение', 'options'=>array(''=>'Ничего не выбрано', 'val_1'=>'Выбор 1', 'val_2'=>'Выбор 2') 
			),
			'select_field2'   => array(
				'type'=>'select', 'title'=>'Выберите значение 2', 'options'=>array('Выбор 1', 'Выбор 2'), 'desc'=>'Выбор, где не указывается значение value для тегов option'
			),
			'checkbox_field'  => array(
				'type'=>'checkbox', 'title'=>'Галочка', 'desc'=>'отметьте, если хотите :)' 
			),
			'checkbox_field2' => array(
				'type'=>'checkbox', 'desc'=>'< только описание для галочки, без заголовка' 
			),
			'radio_field'     => array(
				'type'=>'radio', 'title'=>'Переключатель', 'desc'=>'Выберите одно из значений', 'options'=>array(''=>'Ничего не выбрано', 'good'=>'хорошо', 'bad'=>'плохо') 
			),
			'radio_field2'    => array(
				'type'=>'radio', 'desc'=>'Переключатель без заголовка', 'options'=>array(''=>'Не выбрано', 'good'=>'хорошо', 'bad'=>'плохо') 
			),
			'wp_editor_field' => array(
				'type'=>'wp_editor', 'title'=>'Текстовое поле с редактором TinyMce'
			),
			'wp_editor_field2' => array(
				'type'=>'wp_editor', 'title'=>'Текстовое поле с редактором WordPress, без TinyMce', 'options'=>array('tinymce'=>0) // список настроек: http://wp-kama.ru/function/wp_editor
			),
			'hidden_field' => array(
				'type'=>'hidden', 'val'=>'foo'
			),
			'' => array('type'=>'text', 'title'=>''), // заготовка
		),
	)
);

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

post-meta-fields

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

post-meta-fields2

меню

#2 Блоки для указанных типов записей

Когда нужно создать метабокс с метаполями только для указанных типов записи, укажите параметр 'post_type' => array('post','page') в котором перечислите нужные типы записей.

class_exists('Kama_Post_Meta_Box') && new Kama_Post_Meta_Box(
	array(
		'id'         => 'my',
		'title'      => 'Мои произвольные поля',
		'post_type'  => array('page', 'my_type'), // показывать только на страницах типа: page и my_type
		'fields'     => array(
			'text_field' => array( 'title' => 'Текстовое поле' ),
		),
	)
);

#3 Своя функция вывода поля

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

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

<?php
class_exists('Kama_Post_Meta_Box') && new Kama_Post_Meta_Box(
	array(
		'id'         => 'my',
		'title'      => 'Мои произвольные поля',
		'fields'     => array(
			'special_field' => array(
				'title'=>'Текстовое поле', 'callback'=>'special_field_out_function',
				// функция очистки полей
				'sanitize_func'=>function($array){ return array_map('sanitize_text_field', $array); }
			),
		),
	)
);

// функция вывода для поля
function special_field_out_function( $args, $post, $name, $val ){
	/*
		$args = Array
		(
			[type] => 
			[title] => Текстовое поле
			[desc] => 
			[placeholder] => 
			[id] => 
			[class] => 
			[attr] => 
			[val] => 
			[options] => 
			[callback] => special_field_out_function
			[key] => special_field
			[field_patt] => %s
			[title_patt] => <strong class="tit">%s</strong>
			[desc_patt] => <br><span class="description" style="opacity:0.6;">%s</span>

		)

		$post = Объект записи

		$name = 92a9f92_meta[my_special_field]

		$val = Текущее значение метаполя
	*/
	ob_start();
	?>
	<div class="special_field_wrap">
		Ящик 1: <input type="text" name="<?= $name ?>[box1]" value="<?= esc_attr( @ $val['box1'] ) ?>">
		Ящик 2: <input type="text" name="<?= $name ?>[box2]" value="<?= esc_attr( @ $val['box2'] ) ?>">
		Ящик 3: <input type="text" name="<?= $name ?>[box3]" value="<?= esc_attr( @ $val['box3'] ) ?>">
	</div>
	<?php
	return ob_get_clean();
}

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

Произвольный вывод метаполя

Все данные будут храниться в поле my_special_field в виде массива array( 'box1'=>'яблоки', 'box1'=>'апельсины', 'box1'=>'груши' ).

меню

#4 Точные названия полей без префикса

Когда нужно, чтобы названия метаполей были точно такие же какие вы указали, добавьте в начало id нижнее подчеркивание _.

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

Например, у вас уже есть метаполя: foo, bar, views, title и для них нужно создать метабокс:

class_exists('Kama_Post_Meta_Box') && new Kama_Post_Meta_Box(
	array(
		'id'         => '_my', // "_" - значит, что id не будет добавляться в название поля
		'title'      => 'Мои точные произвольные поля',
		'fields'     => array(
			'foo' => array( 'title' => 'Поле foo' ),
			'bar' => array( 'title' => 'Поле bar' ),
			'views' => array( 'title' => 'Поле views' ),
			'title' => array( 'title' => 'Поле title' ),
		),
	)
);

Получим такой метабокс:

Точные произвольные поля

меню

#5 Очистка значений перед сохранением

Класс автоматически очищает все поля и защищает от XSS атак. Но иногда может быть нужно, очистить определенное поле как-то особенно. В этом случае укажите навзание функции очистки в параметре 'save_sanitize' или используйте фильтр "kpmb_save_sanitize_{id}". Если указана функция или хук очистки, то класс никак не очищает сохраняемые данные, очистка всех полей должна быть в вашей функции очистки.

Допустим, мы создаем поле в котором все символы должны быть прописными, а если они указаны как строчные, то автоматом преобразуем их в прописные и сохраним:

class_exists('Kama_Post_Meta_Box') && new Kama_Post_Meta_Box(
	array(
		'id'     => 'my',
		'title'  => 'Мои произвольные поля',
		'save_sanitize' => 'my_metabox_sanitize_function',
		'fields' => array(
			'foo_field' => array( 'title'=>'Некое поле' ),
			'for_esc' => array( 'title'=>'Специальное поле', 'desc'=>'Поле которое должно содержать только прописные символы.' ),
		),
	)
);

// функция очистки всех полей
function my_metabox_sanitize_function( $metas, $post_id ){
	/*
		$metas = сохраняемые поля в массиве
		$post = ID записи
	*/

	// очищаем нужное поле
	foreach( $metas as $key => & $val ){
		// наше поле
		if( $key == 'my_for_esc' )
			$val = mb_strtoupper( $val );
		// все остальные поля
		else
			$val = sanitize_text_field( $val );
	}

	return $metas;
}

В результате получим:

post-meta-fields7

Еще один пример функции очистки

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

function save_sanitize_function( $metas, $post_id ){
	array_walk_recursive( $metas, function( & $val, $key ){
		// очистка отдельного поля my_video - разрешим оставить в нем тег iframe
		if( $key == 'my_video' ){
			$val = addslashes( wp_kses( stripslashes( $val ), array(
				'iframe' => array( 'src' => true, 'width' => true, 'height' => true, 'frameborder' => true, 'marginwidth' => true, 'marginheight' => true, 'scrolling' => true, 'title' => true, ),
			) ) );
		}
		// has tags
		elseif( false !== strpos($val, '<') )
			$val = addslashes( wp_kses( stripslashes( $val ), 'post' ) ); // default ?
		else
			$val = sanitize_text_field( $val );
	} );

	return $metas;
}
меню

#6 Темы оформления: настройка html и css каждого поля

Можно указать как должны выводиться поля в метабоксе. Для этого есть параметр theme - тема оформления.

В этом параметре можно указать строку или массив:

  • table (по умолчанию) - поля будут выведены в табличной форме.
  • line - поля будут выведены линиями. Во всю ширину метабокса, где идет заголовок поля, а под ним само поле.
  • array() - указывая массив, вы можете сами определить все виды оборачивающих тегов для каждого элемента поля. В массиве можно указать следующие параметры:

    • css — CSS стили метабокса. Например: '.my_field{ margin:1em; }'
    • fields_wrap — формат обёртки всего метаблока (всех полей)
    • field_wrap — формат обёртки поля (вместе с заголовком и полем)
    • title_patt — формат обёртки заголовка поля
    • field_patt — формат обёртки самого поля (только input...)
    • desc_patt — формат обёртки описания поля
class_exists('Kama_Post_Meta_Box') && new Kama_Post_Meta_Box(
	array(
		'id'         => 'my',
		'title'      => 'Мои произвольные поля',

		'theme' => array(
			'css'        => '.my_field_wrap{ margin-bottom:1em; } .my_field_desc{ opacity:0.5; } .my_field_tit{ font-weight:bold; margin-bottom:.3em; }',
			'fields_wrap' => '%s',
			'field_wrap' => '<div class="my_field_wrap %1$s">%2$s</div>', // '%2$s' будет заменено на html поля
			'title_patt' => '<div class="my_field_tit">%s</div>', // '%s' будет заменено на заголовок
			'field_patt'  => '%s',
			'desc_patt'  => '<span class="my_field_desc"> %s</span>', // '%s' будет заменено на текст описания
		),

		'fields'     => array(
			'text_field'      => array(
				'title' => 'Текстовое поле'
			),
			'number_field'    => array(
				'type'=>'number', 'title'=>'Числовое поле', 'desc'=>'Число от 0 до 5', 'attr'=>'min="0" max="5"'
			),
			'textarea_field'  => array(
				'type'=>'textarea', 'title'=>'Большое текстовое поле', 'desc'=>'Описание чего-либо. Можно использовать html теги.'
			),
			'select_field'    => array(
				'type'=>'select', 'title'=>'Выберите значение', 'options'=>array(''=>'Ничего не выбрано', 'val_1'=>'Выбор 1', 'val_2'=>'Выбор 2') 
			),
			'checkbox_field'  => array(
				'type'=>'checkbox', 'title'=>'Галочка', 'desc'=>'отметьте, если хотите :)' 
			),
			'radio_field'     => array(
				'type'=>'radio', 'title'=>'Переключатель', 'desc'=>'Выберите одно из значений', 'options'=>array(''=>'Ничего не выбрано', 'good'=>'хорошо', 'bad'=>'плохо') 
			),
			'wp_editor_field2' => array(
				'type'=>'wp_editor', 'title'=>'Текстовое поле с редактором WordPress, без TinyMce', 'options'=>array('tinymce'=>0) // список настроек: http://wp-kama.ru/function/wp_editor
			),
			'' => array('type'=>'text', 'title'=>''), // заготовка
		),
	)
);

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

post-meta-fields8

меню

#7 Блоки с одинаковыми id

Класс позволяет создавать два и более разных блока с одинаковыми id. Следующий пример создаст 2 разных блока метаполя которых будут иметь одинаковый префикс: my_:

if( class_exists('Kama_Post_Meta_Box') ){
	new Kama_Post_Meta_Box( array(
		'id'         => 'my',
		'title'      => 'Мои произвольные поля',
		'fields'     => array(
			'text_field' => array( 'title' => 'Текстовое поле' ), // название поля: my_text_field
		),
	) );
	new Kama_Post_Meta_Box( array(
		'id'         => 'my',
		'title'      => 'Мои произвольные поля 2',
		'fields'     => array(
			'foo_field' => array( 'title' => 'Текстовое поле' ), // название поля: my_foo_field
		),
	) );
}

#8 Отключение метабокса по условию

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

В этот параметр нужно передать название функции, которая будет срабатывать перед выводом метабокса и отключать его, если срабатывает описанное в функции условие. Также, вместо названия функции, можно передать анонимную функцию (замыкание). Далее, если указанная функция, что-нибудь возвращает, то метабокс будет отключен.

А теперь примеры.

меню

#1. Метабокс записи для указанной рубрики

Допустим нам нужно вывести метабокс, только если запись находится в рубрике с ID = 2, т.е. его нужно скрыть для всех рубрик кроме 2:

new Kama_Post_Meta_Box( array(
	'id'        => 'mybox',
	'title'     => 'Виден для рубрики 2',
	'post_type' => 'post',
	'disable_func' => function($post){
		if( ! in_category(2, $post) ) return 'отключить';
	},
	'fields'    => array(
		'fname' => array( 'type'=>'text', 'title'=>'Поле-поле' ),
	),
) );

#2. Метабокс записи для всех рубрик кроме указанной

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

new Kama_Post_Meta_Box( array(
	'id'        => 'mybox',
	'title'     => 'Виден для всех рубрик, кроме 2',
	'post_type' => 'post',
	'disable_func' => function($post){
		if( in_category(2, $post) ) return 'отключить';
	},
	'fields'    => array(
		'fname' => array( 'type'=>'text', 'title'=>'Поле-поле' ),
	),
) );

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

меню

Опрос

меню

Как вы создаете произвольные поля?

  • Добавить ответ
меню

Плагины

  1. Крутой плагин для создания метаполей: Meta Box

  2. Хороший плагин для создания метаполей: Custom Field Suite

  3. Еще один хороший, но огромный: Advanced Custom Fields
68 комментов
Полезные 6 Все
  • Артём

    А можно реализовать, чтобы в поле select выводились записи определенной категории?

    1
    Ответить3 года назад #
    • Kama7629

      В массив передаваемых параметров классу, в параметр поля options укажи динамические данные - получи нужные рубрики и укажи их там. А класс выведет все что указанно.

      Для этого возможно еще нужно будет вызывать класс чуть позднее, через хук init.

      Ответить3 года назад #
      • Артём

        А можно пожалуйста поподробнее насчет динамических данных. Я честно говоря не могу догнать sad

        Ответить3 года назад #
        • Kama7629

          Например, у нас есть поле селект:

          //...
          			'select_field'    => array(
          				'type'=>'select', 'title'=>'Выберите значение', 'options'=>array(''=>'Ничего не выбрано', 'val_1'=>'Выбор 1', 'val_2'=>'Выбор 2') 
          			),

          Надо динамикой передать выбор селекта - записи из рубрики 25

          // Соберем данные заранее
          $_options = array(''=>'Ничего не выбрано');
          $posts = get_posts( array('cat'=>25) );
          foreach( $posts as $pp ){
          	$_options[ $pp->ID ] = esc_html( $pp->post_title );
          }
          
          //...
          			'select_field'    => array(
          				'type'=>'select', 'title'=>'Выберите значение', 'options'=>$_options 
          			),
          1
          Ответить3 года назад #
  • Тимофей

    Здравствуйте!
    Подскажите пожалуйста каким образом можно реализовать загрузку файлов используя произвольные поля (прикрепление файла к записе)?
    Может у вас в будущем получится включить возможность создания такого типа поля в свой класс?

    P.S. спасибо вам за замечательный сайт и отдельно за эту статью!

    -1
    Ответить3 года назад #
    • Kama7629

      Куда надо загружать файл? В медиатеку и выводить ссылку на загруженный файл в блоке произвольных полей? Это сделать не сложно, но нужно написать отдельную функцию для этого, её как модуль можно к этому классу сделать. У меня времени нет пока этим заниматься, а конкретно этого я пока не делал для этого класса, а то бы поделился...

      1
      Ответить3 года назад #
  • Все круто, только не хватает добавления картинок

    1
    Ответить2.6 года назад #
  • Сергей

    Kama,
    Первое:
    когда создаешь поля в классе но выводишь их через собственную функцию локализация срабатывает и текстовые переменные выводятся на нужном языке, а когда через встроенную возможность, текстовые переменные выводятся без перевода.
    Второе:
    если тему задаешь line - то все поля выводятся по порядку их создания, а если table - то поля со своей функцией вывода выводятся вверху и рушится вся последовательность полей
    Как это побороть?
    С уважением Sergey Terr

    Ответить2.5 года назад #
    • Kama7629

      Первое не понял. Покажи кодом. Похоже в момент регистрации массива полей ты юзаешь __() но на этот момент mo файл еще не подключен, поэтому перевода нет. Тебе нужно позднее инициализировать поля...

      Второе поправил, была недоработка кода... Обнови код до версии 1.8.0

      1
      Ответить2.5 года назад #
      • Сергей

        Да именно __() так юзаю, а когда можно - позднее инициализировать?

        Ответить2.5 года назад #
  • Серге

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

    Ответить2.5 года назад #
    • Kama7629

      Это защита... Укажи для этого поля 'sanitize_func' => 'none' так поле при сохранении не будет очищаться...

      1
      Ответить2.5 года назад #
  • Alex

    Стоит плагин Yoast SEO, но по факту я использую только одну из его функций - прописание тайтла. Очень хочу избавиться от этого комбайна с целью минимизации нагрузки на сервер. Как можно автоматизировать перенос инфы из плагина в произвольное поле созданное по инструкции ?

    -1
    Ответить2.1 года назад #
    • @ campusboy3430 www.youtube.com/c/wpplus

      Этот плагин хранит данные в произвольных полях под именами:

      • _yoast_wpseo_title - title страницы
      • _yoast_wpseo_metadesc - description страницы

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

      1
      Ответить2.1 года назад #
    • Kama7629

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

      Ответить2.1 года назад #
    • валерик

      выкидывай скорее это говнище под названием yoast !!))

      1
      Ответить5 мес назад #
  • Алексей

    Пожалуйста подскажите как добавить метабокс к записи с выбором всех статей на сайте + поиск по статьям. В разделе Внещний Вид ---> Меню такой есть - это Записи, тоесть надо чекбоксом отметить запись или же найти нужную через поиск. Мне надо вывести метабокс, который будет позволять выбрать селектом статью, но при этом чтобы был еще и поиск так как статей много. Этот вопрос мне поможет решить плагин ACF PRO в нем есть такой Тип поля как "Одна запись" с выводом того, что мне как раз и надо, но я не хочу использовать плагин для этого.

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

    Ответить2 года назад #
    • Kama7629

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

      Ответить2 года назад #
  • Еще заметил проблемки с локализацией. Если прописываю так:

    'fields'     => array(
    		'title' => array(
    			'type'   =>'text',
    			'title'  => _e('Title of product', 'my_theme'),,
    			'desc'   =>'Заголовок страницы (рекомендуется 70 символов)',
    			'attr'   =>'style="width:99%;"'
    		),

    То метабокс не выводится...

    Ответить2 года назад #
    • @ campusboy3430 www.youtube.com/c/wpplus

      Это проблемы не с локализацией, а знаниями. Почитайте, пожалуйста, что делает функция _e(). Там ответ в самой первой строке.

      P.S.: если всё равно было не понятно, то просто используйте функцию __().

      1
      Ответить2 года назад #
      • Хмм. Вы правы. Почитал описание обех функций, так и не понял, почему второй работает, а первый нет, функции вроде аналогичные.

        Ответить2 года назад #
        • @ campusboy3430 www.youtube.com/c/wpplus

          Не совсем. _e() - сразу ВЫВОДИТ на экран перевод, то есть в наш массив это значение даже не попало, php "выплюнул" результат перевода на экран и дело с концом. Итого, в title попал вакуум, пустота, null.

          А __() - ВОЗВРАЩАЕТ переведенную строку на дальнейшую обработку. В нашем случае она нужна в массиве для title и дальше используется по коду.

          2
          Ответить2 года назад #
  • Можно узнать коротко, почему использовать именно это, а не тот же Meta Box где есть все, и файлы, и гугл карты, и картинки?

    Только в случае когда нужно простые текстовые поля, верно?

    Ответить1.7 года назад #
    • Kama7629

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

      Кое в чем он лучше, так как позволяет полностью изменить вывод метабокса когда надо что-то нестандартное (но это редко надо) или полностью изменить сохранение, например когда при сохранении значения нужно сделать еще какие-то операции.

      Этот класс хорошо может дополнять тот-же метабокс.

      1
      Ответить1.7 года назад #
  • Я еще нашел вот такой интересный сервис! Много чего генерирует. Бесплатно, в отличие от Generate WP https://www.wp-hasty.com/tools/wordpress-metabox-generator/

    1
    Ответить1.6 года назад #
Здравствуйте, !     Войти . Зарегистрироваться