WordPress как на ладони
Наставник Трепачёв Д.П., phphtml.net wordpress jino

Расширяемость Carbon Fields

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

На стороне клиента работает JavaScript библиотека Backbone. Использование Backbone обеспечило Carbon Fields прочной основой для создания масштабируемого приложения.

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

Шаблоны - как изменить шаблон (вид) поля в Carbon Fields?

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

Примеры

Новое поле

use Carbon_Fields\Field;

class Example_Field extends Field {
	// Основной шаблон
	function template() {
		?>
		<input id="{{{ id }}}" type="text" name="{{{ name }}}" value="{{ value }}" class="regular-text" />
		<span>Это пример поля. </span>
		<?php
	}
}

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

add_template( $name, $callback )
use Carbon_Fields\Field;

class Example_Field extends Field {
	function admin_init() {
		// Добавляем шаблон описания
		$this->add_template($this->get_type() . '-Description', array($this, 'template_description'));
	}
	...
	// Шаблон описания
	function template_description() {
		?>
		<div class="carbon-description {{{ value ? '' : 'hidden' }}}">
			<p>Какая-нибудь подсказка для пользователя.</p>
		</div>
		<?php
	}
}
к началу

Новый контейнер

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

В папке с контейнерами /carbon-fields/core/Container/ создаем файл My_Theme_Options_Container.php:

<?php

namespace Carbon_Fields\Container;

class My_Theme_Options_Container extends Theme_Options_Container {
	protected function clear_string( $string ) {
		$string = sanitize_title ( $string );
		return preg_replace( array( '~ +~', '~[^\w\d-]+~u', '~-+~' ), array( '-', '-', '-' ), strtolower( remove_accents( $string ) ) );
	}
}

В пользовательском файле пишем, например:

Container::make( 'my_theme_options', 'Мой пульт' )
		 ->add_fields( array(
			 Field::make( 'text', 'crb_facebook_url' ),
			 Field::make( 'textarea', 'crb_footer_text' )
		 ) );

И теперь вместо ссылки:

/wp-admin/admin.php?page=crbn-Мой-пульт.php

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

/wp-admin/admin.php?page=crbn-%D0%9C%D0%BE%D0%B9-%D0%BF%D1%83%D0%BB%D1%8C%D1%82.php

Станет такой (при условии работы плагинов транслитерации):

/wp-admin/admin.php?page=crbn-moj-pult.php
к началу

Синтаксис шаблонов

Выполнить произвольный код JavaScript.

<# ... #>

Вставить значение.

{{ ... }}

Вставить значение в виде очищенного HTML.

{{{ ... }}}

Переменные шаблона

Чтобы переменные были доступны в шаблоне, используется PHP метод to_json(). Вот пример добавления двух новых переменных (количество строк и высота поля):

//  PHP класс поля Textarea
class Textarea_Field extends Field {
	protected $height = 170;
	protected $rows = 0;
	...
	function to_json($load) {
		$field_data = parent::to_json($load);

		$field_data = array_merge($field_data, array(
			'rows'   => $this->rows,
			'height' => $this->height,
		));

		return $field_data;
	}
}

Переменные шаблона также могут быть добавлены с помощью JavaScript путем расширения объекта templateVariables. Это следует сделать на событии field:beforeRender. Пример:

// File Field Backbone View
carbon.fields.View.File = carbon.fields.View.extend({
	initialize: function() {
		carbon.fields.View.prototype.initialize.apply(this);

		this.on('field:beforeRender', this.loadDescriptionTemplate);
	},
	...
	/**
	 * Loads the description template and sets it as a variable ("description") for the base template
	 */
	loadDescriptionTemplate: function() {
		var type = this.model.get('type');
		var descTemplate = carbon.template(type + '-Description');

		_.extend(this.templateVariables, {
			description: descTemplate(this.templateVariables)
		});
	}
});
к началу

Хуки в Carbon Fields

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

Если плавайте в теме хуков, советуем прочитать обучающую статьи о том, как работают хуки в WordPress.

Основные

(action) carbon_register_fields

Вызывается до регистрации полей.

(action) carbon_after_register_fields

Вызывается после того, как все поля зарегистрированы.

Шаблоны

(filter) carbon_template( $html, $name )

Применяется к шаблону html перед тем, как поместить его в футер админки.

(filter) carbon_template_{template-name}( $html )

Такой же, как carbon_template, только можно указать имя шаблона.

Контейнер для опций темы

(filter) carbon_{container-title}_button_label( $label )

Позволяет изменить текст кнопки "Сохранить изменения" на произвольный.

Объявление фильтра в коде Carbon Fields:

$filter_name  = 'carbon_' . str_replace( '-', '_', sanitize_title( $this->title ) ) . '_button_label';
$button_label = apply_filters( $filter_name, __( 'Save Changes', 'carbon-fields' ) );

Пример создания контейнера:

// Создаем страницу настроек темы
Container::make( 'theme_options', 'My Options' )
		 ->add_fields(array(
			 Field::make( 'text', 'facebook_url', 'Ссылка на Фейсбук' ),
			 Field::make( 'textarea', 'footer_text', 'Текст в футере' )
		 ));

Пример изменения текста кнопки на основе примера выше:

Пример изменения текста кнопки сохранения настроек темы

// Изменяем текст кнопки "Сохранить изменения"
add_filter( 'carbon_my_options_button_label', function( $label ){
	return 'Сохранить мои настройки';
});

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

// Создаем страницу настроек под названием "Опции"
Container::make('theme_options', 'Опции')

// В коде плагина посмотрим, какой $label формируется
$filter_name  = 'carbon_' . str_replace( '-', '_', sanitize_title( $this->title ) ) . '_button_label';
var_dump( $filter_name );

// Получим
string(50) "carbon_%d0%be%d0%bf%d1%86%d0%b8%d0%b8_button_label"

// Если использовать такие плагины Cyr to Lat и подобные, получим
string(26) "carbon_optsii_button_label"

// Изменяем текст кнопки "Сохранить изменения" на основе предыдущего примера
add_filter('carbon_optsii_button_label', function($label){
	return 'Сохранить мои настройки';
});

Универсальное решение проблемы - использовать такую же конструкцию, что и в самом плагине:

add_filter( 'carbon_' . str_replace( '-', '_', sanitize_title( 'Опции' ) ) . '_button_label', function($label){
	return 'Сохранить мои настройки';
});
к началу

Поля для взаимоотношений и ассоциаций

(filter) carbon_relationship_title( $title, $name, $id, $type, $subtype )

Позволяет изменять заголовок элементов отношения / ассоциации. Полезно при реализации пользовательских отношений / связей. Принимает следующие параметры:

  • $title - заголовок пункта
  • $name - имя поля
  • $id - ID поста, таксономии и т.д.
  • $type - основной тип объекта (post, term, user, comment и т.д)
  • $subtype - подтип, дополняющий основной (page, post, category и т.д)
// Сделаем текст пунктов курсивом
add_filter( 'carbon_relationship_title', function($title){
	return "<i>{$title}</i>";
});

// Сделаем текст пунктов курсивом только у относящихся к постам (записям, страницам и т.д.)
add_filter('carbon_relationship_title', 'change_carbon_relationship_title', 10, 5);
function change_carbon_relationship_title($title, $name, $id, $type, $subtype){
	return ( $type == 'post' ) ? "<i>{$title}</i>" : $title;
}

// Сделаем текст пунктов курсивом и поменяем его цвет, в зависимости от принадлежности
add_filter('carbon_relationship_title', 'change_carbon_relationship_title', 10, 5);
function change_carbon_relationship_title($title, $name, $id, $type, $subtype){
	$color = 'black';

	// Записи
	if ( $subtype == 'post' )
		$color = 'indigo';

	// Страницы
	if ( $subtype == 'page' )
		$color = 'green';

	// Пользователи
	if ( $type == 'user' )
		$color = 'orange';

	// Комментарии
	if ( $type == 'comment' )
		$color = 'red';

	// Рубрики
	if ( $subtype == 'category' )
		$color = 'blue';

	return "<i style='color: {$color};'>{$title}</i>";
}

Использование фильтра carbon_relationship_item_label

(filter) carbon_relationship_comment_length( $number, $name )

Заголовок элемента, относящийся к комментарию строится из части его текста. Данный хук позволяет изменить количество символов, видимых из текста комментария в элементе. Принимает следующие параметры:

  • int $number - количество символов (по умолчанию 30)
  • string $name - Имя поля отношения / ассоциации.

Пусть у нас есть поле взаимоотношений под именем my_association и

// Меняем количество знаков у заголовка комментария
function change_relationship_comment_length($number, $name){
	return 20;
}
add_filter('carbon_relationship_comment_length', 'change_relationship_comment_length', 10, 2);

/**
 * Меняем количество знаков у заголовка комментария только у поля ``my_association``.
 * 
 * Не забывайте - плагин добавляет нижнее подчеркивание к имени поля при сохранении.
 */
function change_relationship_comment_length($number, $name){
	if( $name = '_my_association' )
		$number = 15;

	return $number;
}
add_filter('carbon_relationship_comment_length', 'change_relationship_comment_length', 10, 2);

(filter) carbon_relationship_options_{name}_post_{post_type}( $options )

Позволяет изменять доступные параметры поля отношений или ассоциаций с именем {name} и типом поста {post_type}.

К примеру, мы создали поле ассоциаций с именем my_association и указали отображаться в типе поста page (на страницах), тогда имя фильтра будет:

// Для страницы
carbon_relationship_options__my_association_post_page

Фильтр в коде плагина объявлен следующим образом:

$args = apply_filters( $filter_name, array(
	'post_type'        => $type['post_type'],
	'posts_per_page'   => -1,
	'fields'           => 'ids',
	'suppress_filters' => false,
) );

$posts = get_posts( $args );

Это значит, что мы можем вмешаться в запрос путем изменения его параметров, к примеру ограничить загрузку постов по количеству:

add_filter('carbon_relationship_options__my_association_post_page', 'change_carbon_relationship_options', 10, 1);
function change_carbon_relationship_options( $options ){
	$options['posts_per_page'] = 3;
	return $options;
}

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

(filter) carbon_relationship_options_{name}_taxonomy_{taxonomy}( $options )

Принцип работы, как и у предыдущего фильтра. Позволяет изменять доступные параметры поля отношения / ассоциации с именем {name} из таксономии {taxonomy}.

Всё тот же пример с полем my_association:

carbon_relationship_options__my_association_taxonomy_category

Объявление хука в коде плагина:

$args = apply_filters( $filter_name, array(
	'hide_empty' => 0,
	'fields' => 'id=>name',
) );

$terms = get_terms( $type['taxonomy'], $args );

(filter) carbon_relationship_options_{name}_user( $options )

Всё работает по тем же правилам, что и предыдущие подобные хуки.

Объявление хука в коде плагина:

$args = apply_filters( $filter_name, array(
	'fields' => 'ID',
) );

$users = get_users( $args );

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

add_filter('carbon_relationship_options__my_association_user', 'change_carbon_relationship_options_user', 10, 1);
function change_carbon_relationship_options_user( $options ){
	$options['exclude'] = get_current_user_id();
	return $options;
}

(filter) carbon_relationship_options_{name}_comment( $options )

Работает по тем же правилам, что и предыдущие подобные хуки.

Объявление хука в коде плагина:

$args = apply_filters( $filter_name, array(
	'fields' => 'ids',
) );

$comments = get_comments( $args );

(filter) carbon_relationship_options( $options, $name )

Общий фильтр, через который проходит весь массив опций полей для ассоциаций и связей.

Переменная $name имя поля с подчеркиванием в начале, к примеру _my_association.

В переменной $options находится весь массив данных, вот выдержка из подобного массива:

    [0] => Array
		(
			[id] => 1
			[title] => admin
			[type] => user
			[subtype] => user
			[label] => user
			[is_trashed] => 
			[edit_link] => http://wp-test.ru/wp-admin/profile.php
		)

	[1] => Array
		(
			[id] => 2
			[title] => user1
			[type] => user
			[subtype] => user
			[label] => user
			[is_trashed] => 
			[edit_link] => http://wp-test.ru/wp-admin/user-edit.php?user_id=2
		)

	[2] => Array
		(
			[id] => 16
			[title] => Ello! Pretend you're reading t...
			[type] => comment
			[subtype] => comment
			[label] => comment
			[is_trashed] => 
			[edit_link] => http://wp-test.ru/wp-admin/comment.php?action=editcomment&c=16
		)

К примеру, не прибегая к редактирования файла перевод вы можете изменить label на любой желаемый в соответствии с type. А может хотите на основе type добавить в массив новые данные и потом использовать их в шаблоне вывода поля в админке.

к началу

Поле для Gravity Form

(filter) crb_gravity_form_options($options)

Фильтр даёт возможность изменить параметры выпадающего списка, в котором пользователь выбирает ту или иную форму, предоставляемую плагином Gravity Form. О данном поле читайте в статье о произвольных полях Carbon Fields.

add_filter('crb_gravity_form_options', 'crb_my_gravity_form_options');
function crb_my_gravity_form_options($options) {
	// Изменит дефолтный текст "Нет формы"
	$options[0] = 'Не отображать форму';

	return $options;
}

Поле для карты Google

(filter) carbon_map_api_key($api_key)

У плагина есть свой ключ для работы с API картами Google, но если вам нужно использовать собственный - данный фильтр поможет:

add_action( 'carbon_map_api_key', 'crb_get_gmaps_api_key' );
function crb_get_gmaps_api_key( $api_key ) {
	return 'впишите сюда свой ключ';
}
campusboy 1709wp-plus.ru
WordPress-разработчик. Разработка сайтов и лендингов. Доработка существующих проектов. Сопровождение ресурсов.
Расширяемость Carbon Fields 13 комментариев
  • Владимир
    @

    Доброго времени суток. А как собственно организовать перевод полей которые я создаю? Четыре дня рисую разнообразные сюрреалистические конструкции а в ответ тишина. С помощью фильтров ничего добиться не смог, причём в буквальном смысле (они как будто вообще не отрабатывают)

    Ответитьмесяц назад #
    • campusboy1709 cайт: wp-plus.ru

      Привет, Владимир! В таких задач, пример твоего кода, как Рафаэлло - вместо тысячи слов smile

      Ответитьмесяц назад #
      1
      • Владимир
        @

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

        Вот например я хочу менять lable у полей в контейнере управляющем настройками темы.
        Пробовал следующие варианты:

            ...
        	Field::make( 'text', 'url_fb', __('fb','domain') )// Не работает, хотя покакому то только ему ведомому принципу выведет в качестве лейбла "fb" хотя в другом месте без использования кастомного поля отрабатывает всё шикарно
        	...
        
        //Эта злая шайтан конструкция тоже не помогает (не ругайтесь сильно, я просто никак не могу найти способ передать в фильтр или событие сторонние параметры. Вот и приходится крутиться)
        class CarbonL10n {
        	protected static $title;
        	protected static $name;
        
        	public static function translate( $title, $name ) {
        		self::$title = $title;
        		self::$name  = $name;
        //Но вот тут что-то мне подсказывает что я не верно использую этот фильтр (вероятно я недопонимаю "Поля для взаимоотношений и ассоциаций")
        		add_filter(
        			'carbon_relationship_title', function ( $title, $name ) {
        			var_dump( self::$title );
        			var_dump( self::$name );
        
        			return ( self::$name == $name ) ? self::$titleitle : $title;
        		}, 10, 5
        		);
        
        	}
        }
        CarbonL10n::translate(__('fb','domain'), 'url_fb');

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

        Было много разного кода но его быстро не вспомню. Подскажите хотя бы в какую сторону мне рыть?

        P.S. я пробовал выполнить разные виды фильтров чтобы проверить работают ли они вообще. использовал подобный код

        add_filter('carbon_relationship_title', function ($title, $name, $id, $type, $subtype){
        	var_dump( $title );
        	var_dump( $name );
        	var_dump( $id );
        	var_dump( $type );
        	var_dump( $subtype);
        	wp_die('Я хочу увидеть что сюда пришло')
        }, 10, 5);

        и глухо. Ни один фильтр в принципе не заработал

        Ответитьмесяц назад #
        1
        • Владимир
          @

          По моему я накосячил в классе с областью видимости

          Ответитьмесяц назад #
        • campusboy1709 cайт: wp-plus.ru

          У меня проблем не возникло с переводом. Мои действия.

          1 - Подключил Карбон, файл с моими полями и сам файл перевода в functions.php

          require_once get_template_directory() . '/inc/carbon-fields/carbon-fields-plugin.php';
          
          add_action( 'carbon_register_fields', 'crb_register_custom_fields' );
          function crb_register_custom_fields() {
          	// путь к пользовательскому файлу определения поля (полей), измените под себя
          	require_once __DIR__ . '/inc/custom-fields/fields.php';
          }
          
          // Путь к файлу получился wp-content/themes/my-love-theme/languages/ru_RU.mo
          add_action( 'after_setup_theme', function() {
          	load_theme_textdomain( 'my_theme_translate', get_stylesheet_directory() . '/languages' );
          } );

          2 - Код пользовательского файла c полями:

          <?php
          use Carbon_Fields\Container;
          use Carbon_Fields\Field;
          
          Container::make( 'theme_options', 'Настройки темы' )
          	->add_fields( array(
          
          		Field::make('text', 'my_text', __( 'My Text', 'my_theme_translate' ) ),
          
          	) );

          Ну вот и всё. Соответственно файл ru_RU.mo содержит в себе перевод фразы "My Text" -> "Мой текст". Насчёт контейнеров - зачем вообще это нужно?

          Ответитьмесяц назад #
          1
          • Владимир
            @

            Блин, а я регистрировал без хука простым инклюдом. И все проблемы куда-то улетучились. Спасибо smile
            А контейнеры хотелось чтоб всё уже цивильно выглядело
            Ещё раз Спасибо smile

            Ответитьмесяц назад #
            • campusboy1709 cайт: wp-plus.ru

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

              Ответитьмесяц назад #
              1
              • Владимир
                @

                Суть в том что если настройки темы пишутся для русского заказчика то неплохо бы использовать кириллицу для названий пунктов меню и страниц. Выше есть пример как это в принципе можно реализовать при помощи создания нового контейнера.
                Но в данном случае при смене языка скорее всего полетит и контейнер, как минимум будет открываться на другой странице. Я нашел как и где устанавливается заголовок контейнера, и вот по хорошему надо просто классы контейнеров расширить до возможности задать им кастомный заголовок как у поля, передать третий параметр. А ещё лучше найти способ вклиниться в базовый класс контейнера чтоб не возиться со всеми видами контейнеров, но этот способ мне кажется малореалистичным (хотя в php и существуют тонны разнообразных малоизвестных функций).

                Ответитьмесяц назад #
                • Владимир
                  @

                  Заодно может подскажешь похожую библиотеку для работы с постами и категориями

                  Ответитьмесяц назад #
                  • campusboy1709 cайт: wp-plus.ru

                    Какую именно работу с ними ты хочешь проводить?

                    Ответитьмесяц назад #
                    • Владимир
                      @

                      Конкретно интересует создание новых и изменение уже существующий. Желательно на подобие данного плагина. Чтоб юзер не смог их редактировать из админки. понимаю что ручками можно или свои объекты написать но вдруг такое уже существует

                      Ответитьмесяц назад #
  • Tier-X

    Про заготовку не ясно...

    1. Куда кидать файлы заготовки если я использую версию плагина или подключ. либы?
    2. Я не знаю бекбон и ради этого учить его бы не хотелось. Могу я как-то использовать чисты js или jQuery для своего поля/контейнера? Как?
    3. Как лучше всего поменять стили встроенных полей/контейнеров ? написать копии со своими стилями или изменить как-то готовые? Я так понимаю для второго варианта лучше использовать этот плагин как подключаемую библиотеку.

    Есть еще уйма вопросов но они не конкретно по расширяемости...
    В частности я сравниваю этот плагин с Unyson framework есть и большие +ы и -ы относительно его. Например хреново что в карбоне не реализован кастомайзер.

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

    Ответитьмесяц назад #
    • campusboy1709 cайт: wp-plus.ru

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

      1) Все поля Карбона - это php классы, которые через автозагрузчик подключаются. Но ничего тебе не мешает заинклудить новый класс с полем в том же function.php и пользоваться и вызывать своё поле обыкновенным образом. Тоже актуально и для контейнеров.

      2) Как бы Бэкбон и есть JS smile Я к тому, что сделать свой скрипт на JS/jQuery - не проблема, делаешь всё как обычно.

      3) Ты имеешь в виду файл CSS или вообще вёрстку поля? Если вёрстку, то там нет хуков её изменить. Надо наследовать класс и перегружать метод template.

      Насчёт Unyson framework ничего сказать не могу, в планах он стоит, но пока не на первых местах.

      Ответитьмесяц назад #

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

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