Создание пользовательского элемента управления выбором ссылок CMB2 для WordPress
В этом уроке я рассмотрю, как можно создать пользовательский элемент управления для расширения функциональности CMB2 (Custom Meta Boxes 2) от WebDevStudios.
Я разрабатываю веб-сайты (и веб-приложения) с помощью WordPress CMS (система управления контентом), и когда появится новый проект, вы можете гарантировать, что мне потребуется разработать «Пользовательские мета-боксы», чтобы пользователь мог точно контролировать над содержанием и макетом сайтов.
Я подробно расскажу, как я создал средство выбора ссылок управления CMB2 для CMB2 (доступно во всех хороших репозиториях плагинов WordPress). Скриншот которого можно увидеть ниже.
Элемент управления CMB2 Link Picker в действии
Средство выбора ссылок запускает встроенный в WordPress диалог «Вставить/отредактировать ссылку», когда вы нажимаете кнопку «Выбрать». Это можно увидеть на скриншоте ниже:
Нажав кнопку, вы можете выбрать ссылку (или добавить свою)
Я уверен, вы согласитесь, что такой элемент управления невероятно удобен, если вы хотите дать редакторам вашего сайта возможность добавлять ссылку, а также искать в WordPress внутренние ссылки, а не копировать и вставлять ссылки в ссылку. поле.
Введение / История
Для тех, кто не в курсе, мета-окно находится на экране редактора поста WordPress и, скорее всего, будет содержать различные элементы управления формой (текстовые поля, выпадающие списки, флажки и т. д.). Эти элементы управления позволяют пользователям вашего веб-сайта легко изменять пользовательский фрагмент текста или функции на сайте.

Пример метабокса с различными элементами управления формы
WordPress позволяет создавать метаблоки с помощью функций (таких как [add_meta_box](https://developer.wordpress.org/reference/functions/add_meta_box/)), но создание метаблоков таким образом может быть длительным процессом с большим количеством повторений кода (особенно если вы хотите использовать одни и те же элементы управления формы в нескольких проектах).
Почему CMB2?
Некоторые из вас, возможно, слышали о расширенных настраиваемых полях (ACF), которые предоставляют графический интерфейс пользователя (GUI), позволяющий создавать мета-поля непосредственно в WordPress.
На мой взгляд, ACF — не лучший инструмент для масштабируемого веб-решения. Плагин слишком сильно зависит от данных, хранящихся в базе данных. Это вызывает проблемы при развертывании изменений на сайте, поскольку вы не можете просто загрузить свой код и сразу увидеть изменения. Вместо этого вам придется снова выполнять работу в различных средах развертывания (промежуточных, живых и т. д.). Поэтому нам нужно было решение, позволяющее программно создавать метабоксы. Введите CMB2.
Прежде чем мы внедрили CMB2, мы ранее использовали HM Custom Meta Boxes от тех милых людей из Human Made (которые начинались как форк WebDevStudio, предшественника CMB2, «Custom Meta Boxes»).
Нам очень понравились пользовательские мета-боксы HM, и с помощью простейших фрагментов кода мы могли быстро создавать настраиваемые мета-боксы для выполнения практически любых задач!
Пример разметки HM Custom Meta Boxs (это разметка для Instagram Meta Box на первом снимке экрана)
Так зачем переходить на CMB2? Что ж, HM Custom Meta Boxes, к сожалению, не пользовались большой любовью (я разговаривал с их ведущим разработчиком, и он очень-очень занятой человек), в то время как CMB2 продвигалась вперед с новыми функциями, новыми элементами управления и набирала обороты. в сообществе WordPress, и многие люди принимают его и выпускают плагины для его расширения (включая несколько наших партнерских агентств).
Наконец, как вы могли уже догадаться, работа с CMB2 невероятно проста, как мы привыкли, поскольку обе платформы имеют общего предка.
Руководство
Прежде чем мы начнем, у каждого есть свой набор идеалов о том, как создать плагин WordPress, и я пробовал немало, однако учебник «Корневая композиция в WordPress» Тома Дж. Ноуэлла полностью изменил мой способ работы. Я нахожу его подход чистым, простым, и он упрощает дальнейшее обслуживание любого плагина. Если вы возьмете исходный код плагина Link Picker for CMB2, вы увидите методы, которым он учит на практике.
Создание формы
Чтобы создать форму, отображающую средство выбора ссылок, первое, что нам нужно сделать, — это подключиться к cmb2_render_[control_name]действию. Поскольку я назвал этот элемент управления ‘link_picker’, мы можем завершить хук следующим образом:
<?php
add_action( 'cmb2_render_link_picker', array( $this, 'cmb2_render_link_picker' ), 10, 5 );
`
Для тех из вас, кто не очень понимает add_actionхук, он работает следующим образом:
- Первый аргумент
cmb2_render_link_picker— это имя хука, к которому мы хотим подключиться. - Второй аргумент
array( $this, 'cmb2_render_link_picker' )— это функция, которую мы хотим вызвать при запуске этого хука. Обратите внимание, что я оборачиваю это в массив с$thisпервым параметром, потому что я вызываю функцию внутри класса. Если вы не работаете с классами, вы можете просто использовать имя функцииcmb2_render_link_picker.
3., —10это порядок, в котором мы хотим, чтобы функция срабатывала (чем меньше число, тем раньше она срабатывает при вызове действия). - Это
5количество параметров, которые будут переданы в функцию, которую я вызываю (скоро это станет ясно).
Далее мы создаем функцию, которая будет отображать форму:
<?php
public function cmb2_render_link_picker( $field, $value, $object_id, $object_type, $field_type_object) {
…
}
`
Я оставил «DocBlock» в приведенном выше коде, который описывает, что делает каждый из параметров, переданных в cmb2_render_link_picker()функцию.
Обратите внимание, что моя функция начинается с publicобъявления. Это опять же потому, что я работаю в классе. Если вы не работаете с классами, вы можете опустить это.
Значение этого поля передается в функцию через $valueпараметр. В случае с этим полем мы будем проходить через массив, так как наш элемент управления имеет три отдельных элемента:
- Текст
- URL-адрес
- Если ссылка открывается в новом окне (или нет)
Поскольку $valueне всегда устанавливается (например, при первом отображении элемента управления), нам нужно инициализировать его некоторыми значениями по умолчанию. Мы делаем это с помощью следующего фрагмента кода:
<?php
$value = wp_parse_args(
$value,
array(
'text' => '',
'url' => '',
'blank' => 'false',) );
Затем мы можем приступить к рендерингу формы. Вот пример первого элемента управления вводом текста:
<p>
<label for="<?php echo $field_type_object->_id( '_text' ); ?>'">
<?php echo esc_html( $field_type_object->_text( 'link_picker_text', 'Text') ); ?>
</label>
</p>
<?php
echo $field_type_object->input(
array(
'class' => 'cmb_text',
'name' => $field_type_object->_name( '[text]' ),
'id' => $field_type_object->_id( '_text' ),
'value' => $value['text'],) );
?>
Фу! Это выглядит немного грязно, не так ли? Давайте разберем его построчно:
- Открывающий тег абзаца.
- Открывающий тег label для элемента управления, но с
forатрибутом, автоматически устанавливаемым$field_type_object_idпараметром. Это автоматически сгенерирует идентификатор для элемента управления при его отображении. - Текст нашей метки, построенный с использованием текста из массива параметров управления (или возвращается к слову «Текст»).
- Закрывающий тег label
- Тег закрывающего абзаца.
- Начать декларацию PHP
- Используйте элемент управления вводом (часть
$field_type_objectдля создания ввода формы (типом по умолчанию будет текст). - Запустить массив параметров
- Установите класс ввода.
- Установите имя ввода, снова используя
$field_type_objectпомощник. - Установите идентификатор входа на тот же идентификатор, который был установлен на теге метки.
- Получите значение из
$value, так как это массив, нам нужна клавиша «текст» для этого элемента управления. - Закройте массив.
- Закройте функцию ввода.
- Закройте объявление PHP.
Разметка поля формы URL почти такая же, только для использования типов ввода HTML5 мы можем установить дополнительный параметр «type» в «url»:
<?php
…
'type' => 'url',
…
Наконец, мы хотим реализовать раскрывающийся список. Разметка очень знакома:
<?php
echo $field_type_object->select(
array(
'class' => 'cmb_dropdown',
'name' => $field_type_object->_name( '[blank]' ),
'id' => $field_type_object->_id( '_blank' ),
'options' => $blank_options,) );
Обратите внимание, что $field_type_objectфункция, которую мы используем, предназначена selectдля создания раскрывающегося списка. Также обратите внимание, что в строке 6 у нас появился новый атрибут options. В это мы передаем строку «опций». Это генерируется перед этим элементом управления следующим образом:
<?php
$blank_options = '';
$blank_options .= '<option value="false" '. selected( $value['blank'], 'false', false) .'>Opens in same</option>';
$blank_options .= '<option value="true" '. selected( $value['blank'], 'true', false) .'>Opens in new</option>';
Затем все, что нам нужно сделать, это обернуть его в некоторые <div>, и у нас есть полностью визуализированный элемент управления:
<?php
public function cmb2_render_link_picker( $field, $value, $object_id, $object_type, $field_type_object) {
$value = wp_parse_args( $value, array(
'text' => '',
'url' => '',
'blank' => 'false',) );
$blank_options = '';
$blank_options .= '<option value="false" '. selected( $value['blank'], 'false', false) .'>Opens in same</option>';
$blank_options .= '<option value="true" '. selected( $value['blank'], 'true', false) .'>Opens in new</option>';
?>
<div class="link-picker">
<div class="text">
<p>
<label for="<?php echo $field_type_object->_id( '_text' ); ?>'">
<?php echo esc_html( $field_type_object->_text( 'link_picker_text', 'Text') ); ?>
</label>
</p>
<?php
echo $field_type_object->input(
array(
'class' => 'cmb_text',
'name' => $field_type_object->_name( '[text]' ),
'id' => $field_type_object->_id( '_text' ),
'value' => $value['text'],
'desc' => '',) );
?>
</div>
<div class="url">
<p>
<label for="<?php echo $field_type_object->_id( '_url' ); ?>'">
<?php echo esc_html( $field_type_object->_text( 'link_picker_url', 'URL') ); ?>
</label>
</p>
<?php
echo $field_type_object->input(
array(
'class' => 'cmb_text_url',
'name' => $field_type_object->_name( '[url]' ),
'id' => $field_type_object->_id( '_url' ),
'value' => $value['url'],
'type' => 'url',
'desc' => '',) );
?>
</div>
<div class="blank">
<p>
<label for="<?php echo $field_type_object->_id( '_blank' ); ?>'">
<?php echo esc_html( $field_type_object->_text( 'link_picker_blank', 'Window') ); ?>
</label>
</p>
<?php
echo $field_type_object->select(
array(
'class' => 'cmb_checkbox',
'name' => $field_type_object->_name( '[blank]' ),
'id' => $field_type_object->_id( '_blank' ),
'options' => $blank_options,
'desc' => '',) );
?>
</div>
<div class="choose">
<p>
<label>Choose</label>
</p>
<button class="dashicons dashicons-admin-links js-insert-link button button-primary" title="<?php esc_html_e( 'Insert Link', 'cmb' ); ?>">
<span class="screen-reader-text"><?php esc_html_e( 'Choose Link', 'cmb' ); ?></span>
</button>
</div>
</div>
<p class="clear">
<?php echo $field_type_object->_desc();?>
</p>
<?php
}
Вот и все! Мы сделали свой контроль! CMB2 автоматически обрабатывает все данные, которые мы хотим сохранить, так что тут делать нечего.
Стили
На снимке экрана элемента управления, который мы создаем (в верхней части этого поста), к нему применено несколько пользовательских стилей, поэтому он отображается встроенным. Я не буду сегодня рассказывать, как стилизовать форму, но если вам интересно, вы можете скачать плагин и просмотреть исходный код.
Делаем элемент управления повторяемым
Для тех из вас, кто хочет стать немного более продвинутым, вы можете заставить управление работать с повторяемыми областями CMB2. Для этого вам нужно сделать небольшое отображение массива. Для этого используйте код ниже:
<?php
public function cmb2_sanitize_link_picker( $check, $meta_value, $object_id, $field_args, $sanitize_object) {
if (! is_array( $meta_value) ||! $field_args['repeatable']) {
return $check;
}
foreach ($meta_value as $key => $val) {
$meta_value[ $key ] = null;
if(! empty( $val['url'])) {
$meta_value[ $key ] = array_map( 'sanitize_text_field', $val );
}
}
return $meta_value;
}
public function cmb2_types_esc_link_picker( $check, $meta_value, $field_args, $field_object) {
if (! is_array( $meta_value) ||! $field_args['repeatable']) {
return $check;
}
foreach ($meta_value as $key => $val) {
$meta_value[ $key ] = null;
if(! empty( $val['url'])) {
$meta_value[ $key ] = array_map( 'esc_attr', $val );
}
}
return $meta_value;
}
Выбор ссылки
Конечно, весь смысл средства выбора ссылок заключается в том, чтобы интегрировать его в собственные функции выбора ссылок WordPress, делая диалоговое окно «Вставить/отредактировать ссылку» всплывающим при нажатии кнопки «Выбрать».
Чтобы это произошло, мы в значительной степени полагаемся на JavaScript. В частности, я использую jQuery, чтобы все происходило.
Прежде чем я покажу вам JavaScript, который запускает диалоговое окно, мы должны сначала поставить в очередь собственный внутренний JavaScript WordPress, который предварительно загрузит модальные окна и библиотеки, от которых зависит наш код. Это выглядит примерно так:
<?php
global $post_id;
if (isset( $post_id)) {
wp_enqueue_media( array( 'post' => $post_id) );
}
$plugin_js_url = plugins_url( 'js/plugin.js', ROOT );
wp_enqueue_script( 'wholesomecode', $plugin_js_url, array( 'jquery', 'jquery-ui-core', 'jquery-ui-draggable', 'jquery-ui-droppable', 'thickbox', 'wpdialogs' ), '1.0.0', true );
Как видите, многие внутренние библиотеки WordPress используют jQuery для загрузки всплывающего окна, поэтому имеет смысл, чтобы наш триггер всплывающих окон делал то же самое. Это делается с помощью файла, /js/plugin.jsкоторый загружается в строке 10 приведенного выше примера.
jQuery(document).ready(function($) {
var url = $('body');
var text = $('body');
var blank = $('body');
$('body').on('click', '.js-insert-link', function(event) {
event.preventDefault? event.preventDefault(): event.returnValue = false;
event.stopPropagation();
url = $(this).closest('.link-picker').find('input.cmb_text_url ');
text = $(this).closest('.link-picker').find('input.cmb_text ');
blank = $(this).closest('.link-picker').find('input.cmb_checkbox ');
wpActiveEditor = true;
wpLink.open();
wpLink.textarea = url;
return false;
});
$('body').on('click', '#wp-link-cancel, #wp-link-backdrop, #wp-link-close', function(event) {
wpLink.textarea = url;
wpLink.close();
event.preventDefault? event.preventDefault(): event.returnValue = false;
event.stopPropagation();
return false;
});
$('body').on('click', '#wp-link-submit', function(event) {
console.log(text)
var linkAtts = wpLink.getAttrs();
linkAtts.text = $('#wp-link-text').val();
url.val(linkAtts.href);
if( linkAtts.text != '') {
text.val(linkAtts.text);
}
if (linkAtts.target == '_blank') {
blank.prop('checked', true);
} else {
blank.prop('checked', false);
}
wpLink.textarea = url;
wpLink.close();
event.preventDefault? event.preventDefault(): event.returnValue = false;
event.stopPropagation();
return false;
});
});
Используя классы, которые мы обернули вокруг наших элементов управления формы, JavaScript нацеливается на элементы управления и помещает выбранный результат из всплывающего окна выбора ссылок в соответствующие поля элементов управления.
Использование элемента управления
Итак, после просмотра руководства выше и, возможно, после просмотра исходного кода плагина Link Picker для CMB2 или просто загрузки моей версии, вам может быть интересно, как использовать эту штуку с CMB2. Что ж, проще некуда:
<?php
function wholesomecode_create_meta_boxes() {
$prefix = '_profile_';
$cmb = new_cmb2_box(
array(
'id' => 'cta',
'title' => __( 'Call to Action', 'cmb2' ),
'object_types' => array( 'profile' ),
'context' => 'normal',
'priority' => 'low',
'show_names' => true,) );
$field1 = $cmb->add_field(
array(
'name' => __( 'Link Picker', 'cmb2' ),
'id' => $prefix. 'cta_link',
'type' => 'link_picker',) );
}
add_action( 'cmb2_admin_init', 'wholesomecode_create_meta_boxes' );