От автора: для получения максимального результата от урока по разработке плагина для WordPress вы должны понимать базовые понятия, такие как экшены, фильтры, шорткоды, виджеты и объектно-ориентированный дизайн.
Реальный пример – Список филиалов компании
Сразу перейдем к реальному примеру того, как плагин может решать проблемы и улучшать функционал вашего сайта. Зачастую у компаний возникает задача показать адреса различных мест или их офисов. Для каждого места можно создать отдельную страницу, но куда лучше будет реализовать их в виде уникального контента со своими данными на одной странице, и завернуть все это в простой интерфейс.
Данный функционал можно было бы реализовать в дочерней теме, но из-за его универсальности лучше создать плагин, который потом можно будет использовать и на других сайтах.
В примере мы расскажем про разработку плагина, который будет обрабатывать данные и выводить их в виде списка мест. Места будут представлять собой пользовательский тип контента с дополнительными мета данными для хранения информации о местоположении. В плагине будет возможность отображать информацию несколькими способами (отдельная страница мест, виджет со списком мест и шорткод со списком мест).
Настройка
Давайте все настроим, перейдите в папку плагина и создайте следующие папки/файловую структуру
Самый верхний файл wp_simple_location_plugin.php будет главным. Тут мы будем загружать наши стили из папки CSS, а также дополнительные PHP файлы из папки inc.
Основной класс местоположения
В файле wp_simple_location_plugin.php будет прописано ядро плагина. Также в этом файле мы подключим дополнительные файлы, которые нужны для создания виджета и шорткодов.
Непрямой доступ
Рекомендуется закрывать прямой доступ к PHP файлам с помощью проверки на существование константы ABSPATH (если не существует, скрипт прекращает работу). Поместите код ниже прямо после открывающего PHP тега:
1 |
defined( 'ABSPATH' ) or die( 'Nope, not accessing this' ); |
Объявление плагина
Чтобы плагин работал, его сначала необходимо объявить. Объявление выглядит, как набор комментариев для WordPress, в которых содержится информация о плагине. В плагин необходимо добавить код ниже, иначе он просто не появится в менеджере плагинов в WP.
1 2 3 4 5 6 7 8 9 10 11 |
<?php /* Plugin Name: WordPress Simple Location Plugin Plugin URI: //github.com/simonrcodrington/Introduction-to-WordPress-Plugins---Location-Plugin Description: Creates an interfaces to manage store / business locations on your website. Useful for showing location based information quickly. Includes both a widget and shortcode for ease of use. Version: 1.0.0 Author: Simon Codrington Author URI: //www.simoncodrington.com.au License: GPL2 License URI: //www.gnu.org/licenses/gpl-2.0.html */ |
В этом коде вы указываете название плагина, описание, версию и другие данные. Большая часть этих данных будет показываться на странице администрирования, когда пользователь будет активировать плагин.
Класс wp_simple_location Class
Теперь необходимо создать оболочку для класса wp_simple_location. В этом классе будет прописана большая часть функционала плагина, в нем будут храниться наши свойства и методы.
1 2 3 |
class wp_simple_location{ } |
Подключаем файлы шорткодов и виджета
Так как мы будем использовать и виджет и шорткод, то было решено разбить функционал на два отдельных файла.
Скопируйте код ниже в самый конец класса wp_simple_location:
1 2 3 4 |
//шорткоды include(plugin_dir_path(__FILE__) . 'inc/wp_location_shortcode.php'); //виджеты include(plugin_dir_path(__FILE__) . 'inc/wp_location_widget.php'); |
Код выше подключает наши файлы. О них мы поговорим чуть позже.
Свойства класса
Свойства класса это переменные, которые используются в функциях класса. С помощью свойств намного проще получать доступ к часто используемым элементам. В данной функции у нас будет всего одно свойство, массив дней, который будет использоваться для отображения рабочего времени для каждого места. Создадим пустой массив, чуть позже мы заполним его значениями.
1 2 |
//Свойства private $wp_location_trading_hour_days = array(); |
Функция _construct
Функция _construct – важная часть плагина. Это мастер-функция, с помощью которой можно обрабатывать и запускать другие функции.
Функция относится к так называемым magic (магическим) функциям. Магические функции представляют собой специальные функции, добавленные в PHP5, автоматически запускающиеся при определенных условиях. Данная функция срабатывает сразу же после создания экземпляра класса (класс создан и создан его экземпляр в виде переменной).
В плагине данную функцию вы будете использовать для добавления всех экшенов, фильтров и вызова других функций. Хуки можно добавлять и из другого места, но пусть лучше они останутся там. Скопируйте код ниже. Мы пройдемся по каждому элементу, чтобы вы поняли, что тут происходит.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//магическая функция (запускается при создании экземпляра класса) public function __construct(){ add_action('init', array($this,'set_location_trading_hour_days')); //задает рабочие дни (используется типом контента) add_action('init', array($this,'register_location_content_type')); //регистрирует тип контента местоположения add_action('add_meta_boxes', array($this,'add_location_meta_boxes')); //добавляет мета поля add_action('save_post_wp_locations', array($this,'save_location')); //сохраняет местоположение add_action('admin_enqueue_scripts', array($this,'enqueue_admin_scripts_and_styles')); //скрипты и стили администратора add_action('wp_enqueue_scripts', array($this,'enqueue_public_scripts_and_styles')); //публичные стили и скрипты add_filter('the_content', array($this,'prepend_location_meta_to_content')); //вытягивает наши мета данные и показывает их перед контентом register_activation_hook(__FILE__, array($this,'plugin_activate')); //активирует хук register_deactivation_hook(__FILE__, array($this,'plugin_deactivate')); //отключает хук } |
Функции register_activation_hook и register_deactivation_hook используются для встраивания в другие функции при активации и отключении плагина. Мы будем использовать эти функции для проверки правильности добавленного типа контента (наши места) и обработки ссылок (мы будем использовать человекопонятные ссылки).
Настройка рабочих часов для определенного филиала
Наш плагин позволяет администратору устанавливать часы открытия и закрытия филиала для каждого дня недели по отдельности.
Рабочие дни хранятся на стороне back-end’а в свойстве $wp_location_trading_hour_days. Для настройки рабочих дней и часов необходимо вызвать функцию set_location_trading_hour_days.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//Устанавливает стандартны рабочие дни и часы (используется на стороне back-end) public function set_location_trading_hour_days(){ //задаем дни, которые будут использоваться для рабочих часов $this->wp_location_trading_hour_days = apply_filters('wp_location_trading_hours_days', array('monday' => 'Monday', 'tuesday' => 'Tuesday', 'wednesday' => 'Wednesday', 'thursday' => 'Thursday', 'friday' => 'Friday', 'saturday' => 'Saturday', 'sunday' => 'Sunday', ) ); } |
После заполнения значений массива необходимо вызвать фильтр wp_location_trading_hours_days. Т.е. тема или другой плагин смогут переписать рабочие дни магазина (они могут фильтровать массив и добавить поле каникул «holidays», чтобы потом задать для этого периода свое рабочее время).
Настройка рабочих часов заданного филиала
Тут мы создадим наш пользовательский тип местоположений, который будем использовать в плагине. Необходимо указать лейблы и аргументы типа контента и передать наши аргументы в функцию register_post_type. На странице кодекса объясняются все варианты пользовательского типа контента. Для нашего типа нам понадобятся заголовок, редактор и встроенное изображение.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
//регистрация типа контента местоположения public function register_location_content_type(){ //Лейблы типа постов $labels = array( 'name' => 'Location', 'singular_name' => 'Location', 'menu_name' => 'Locations', 'name_admin_bar' => 'Location', 'add_new' => 'Add New', 'add_new_item' => 'Add New Location', 'new_item' => 'New Location', 'edit_item' => 'Edit Location', 'view_item' => 'View Location', 'all_items' => 'All Locations', 'search_items' => 'Search Locations', 'parent_item_colon' => 'Parent Location:', 'not_found' => 'No Locations found.', 'not_found_in_trash' => 'No Locations found in Trash.', ); //аргументы типа постов $args = array( 'labels' => $labels, 'public' => true, 'publicly_queryable'=> true, 'show_ui' => true, 'show_in_nav' => true, 'query_var' => true, 'hierarchical' => false, 'supports' => array('title','thumbnail','editor'), 'has_archive' => true, 'menu_position' => 20, 'show_in_admin_bar' => true, 'menu_icon' => 'dashicons-location-alt', 'rewrite' => array('slug' => 'locations', 'with_front' => 'true') ); //регистрация типа постов register_post_type('wp_locations', $args); } |
После добавления этого кода в меню должна появиться новая строка.
Добавляем мета бокс к новому типу местоположений
Необходимо создать пользовательский мета бокс, который будет показываться на странице местоположений. В этом боксе будут располагаться все дополнительные поля, которые нам понадобятся в виде мета данных (номер телефона, электронная почта, адрес). Внутри функции вызываем функцию add_meta_box() и передаем ей наши аргументы.
1 2 3 4 5 6 7 8 9 10 11 12 |
//добавление мета бокса к местоположениям*/ public function add_location_meta_boxes(){ add_meta_box( 'wp_location_meta_box', //id 'Location Information', //название array($this,'location_meta_box_display'), //функция отображения 'wp_locations', //тип поста 'normal', //Расположение 'default' //Приоритет ); } |
Третье значение add_meta_box – функция, которая будет показывать наш бокс. Тут мы вызываем функцию location_meta_box_display, ее мы создадим следующей.
Функция location_meta_box_display
Эта функция будет вызываться из мета бокса и будет показывать дополнительные поля, которые администратор сможет использовать для записи информации о месте.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
//функция отображения для мета бокса box*/ public function location_meta_box_display($post){ //поле nonce wp_nonce_field('wp_location_nonce', 'wp_location_nonce_field'); //собираем переменные $wp_location_phone = get_post_meta($post->ID,'wp_location_phone',true); $wp_location_email = get_post_meta($post->ID,'wp_location_email',true); $wp_location_address = get_post_meta($post->ID,'wp_location_address',true); ?> <p>Enter additional information about your location </p> <div class="field-container"> <?php //делаем хук перед главными элементами формы do_action('wp_location_admin_form_start'); ?> <div class="field"> <label for="wp_location_phone">Contact Phone</label> <small>main contact number</small> <input type="tel" name="wp_location_phone" id="wp_location_phone" value="<?php echo $wp_location_phone;?>"/> </div> <div class="field"> <label for="wp_location_email">Contact Email</label> <small>Email contact</small> <input type="email" name="wp_location_email" id="wp_location_email" value="<?php echo $wp_location_email;?>"/> </div> <div class="field"> <label for="wp_location_address">Address</label> <small>Physical address of your location</small> <textarea name="wp_location_address" id="wp_location_address"><?php echo $wp_location_address;?></textarea> </div> <?php //рабочие часы if(!empty($this->wp_location_trading_hour_days)){ echo '<div class="field">'; echo '<label>Trading Hours </label>'; echo '<small> Trading hours for the location (e.g 9am - 5pm) </small>'; //проходимся в цикле через все рабочие дни foreach($this->wp_location_trading_hour_days as $day_key => $day_value){ //собираем мета данные о рабочих часах $wp_location_trading_hour_value = get_post_meta($post->ID,'wp_location_trading_hours_' . $day_key, true); //показываем лейблы и инпут echo '<label for="wp_location_trading_hours_' . $day_key . '">' . $day_key . '</label>'; echo '<input type="text" name="wp_location_trading_hours_' . $day_key . '" id="wp_location_trading_hours_' . $day_key . '" value="' . $wp_location_trading_hour_value . '"/>'; } echo '</div>'; } ?> <?php //хук после основных элементов формы do_action('wp_location_admin_form_end'); ?> </div> <?php } |
Что делает функция:
Сперва создается безопасное nonce поле для мета бокса (nonce используется для проверки того, что экшен отправки формы пришел из нужного места).
Поле собираем наши телефоны, почту и адреса (если они есть).
Перед самой формой мы добавляем хук wp_location_admin_form_start. Это позволит другим плагинам или темам встроиться в это место, чтобы добавить дополнительные поля или данные.
Показываем поля телефона, почты и адреса (если есть какие-то данные, то заполняем их).
Показываем список рабочих дней для отдельного мета. Тут администратор может устанавливать рабочие часы для каждого дня в отдельности. Дни могут меняться, если они прикреплены к фильтру wp_location_trading_hours_days.
Перед закрытием формы добавляем хук wp_location_admin_form_end. Это позволит другим плагинам или темам встроиться в этом место и добавить дополнительные поля или данные.
Мета бокс должен показаться на странице места. Выглядеть это будет примерно так:
Регистрация типа контента и обработка правил перезаписи во время активации
Во время первой активации плагина можно вызвать функцию, которая будет выполнена всего раз. Для этого мы будеи использовать функцию plugin_activate.
Мы зарегистрировали тип контента через хук init, но нам все равно нужно вызвать функцию register_location_content_type (чтобы проверить, добавился ли наш тип).
Также мы изменим правила перезаписи, чтобы можно было использовать человекопонятные ссылки (у нас будут ссылки типа example.com/location/mylocation вместо example.com/?p=144)
1 2 3 4 5 6 7 |
//Срабатывает при активации плагина (вызывается единожды) public function plugin_activate(){ //вызываем функцию проверки пользовательского типа контента $this->register_location_content_type(); //обрабатываем ссылки flush_rewrite_rules(); } |
Сброс правил перезаписи при отключении плагина
Функция plugin_deactivate срабатывает при отключении плагина. Так как мы удаляем плагин, а в плагине использовался пользовательский тип контента, то для верности необходимо сбросить правила перезаписи.
1 2 3 4 5 |
// Срабатывает при отключении плагина (вызывается единожды) public function plugin_deactivate(){ //Сброс ссылок flush_rewrite_rules(); } |
Показываем мета данные для конкретного места
Мы создали дополнительные мета данные для всех мест, теперь необходимо создать функцию для отображения этих данных при просмотре каждого отдельного места.
Функция prepend_location_meta_to_content цепляется к фильтру the_content, т.е. мы можем добавить свои данные перед главным контентом страниц.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
public function prepend_location_meta_to_content($content){ global $post, $post_type; //Показываем мета данные только для мест (и если место конкретное) if($post_type == 'wp_locations' && is_singular('wp_locations')){ //собираем переменные $wp_location_id = $post->ID; $wp_location_phone = get_post_meta($post->ID,'wp_location_phone',true); $wp_location_email = get_post_meta($post->ID,'wp_location_email',true); $wp_location_address = get_post_meta($post->ID,'wp_location_address',true); //показываем $html = ''; $html .= '<section class="meta-data">'; //хук для вставки дополнительных данных (в начале формы) do_action('wp_location_meta_data_output_start',$wp_location_id); $html .= '<p>'; //телефон if(!empty($wp_location_phone)){ $html .= '<b>Location Phone</b> ' . $wp_location_phone . '</br>'; } //почта if(!empty($wp_location_email)){ $html .= '<b>Location Email</b> ' . $wp_location_email . '</br>'; } //адрес if(!empty($wp_location_address)){ $html .= '<b>Location Address</b> ' . $wp_location_address . '</br>'; } $html .= '</p>'; //Место if(!empty($this->wp_location_trading_hour_days)){ $html .= '<p>'; $html .= '<b>Location Trading Hours </b></br>'; foreach($this->wp_location_trading_hour_days as $day_key => $day_value){ $trading_hours = get_post_meta($post->ID, 'wp_location_trading_hours_' . $day_key , true); $html .= '<span class="day">' . $day_key . '</span><span class="hours">' . $trading_hours . '</span></br>'; } $html .= '</p>'; } //Хук для вставки дополнительных мета данных (в конце формы) do_action('wp_location_meta_data_output_end',$wp_location_id); $html .= '</section>'; $html .= $content; return $html; }else{ return $content; } } |
Что делает функция:
Функция добавлена к хуку the_content, т.е. она будет запускаться при каждом обновлении страницы. Мы используем глобальные переменные $post и $post_type для проверки, что мы находимся на странице конкретного места
Собираем базовую информацию: почта, телефон и адрес.
Перед отображением мета данных вызываем хук wp_location_meta_data_output_start. С его помощью другие плагины или темы смогут встроиться в этом место и добавлять дополнительные мета данные (если кто-то добавит новое поле к месту, этот хук можно будет использовать для отображения сохраненных данных).
Показываем почту, телефон и адрес.
Пробегаемся по переменной wp_location_trading_hour_days и проверяем ее на рабочие дни. Если они есть, пробегаемся по ним в цикле, сохраняем рабочие часы и показываем их.
Почти в самом конце вызываем экшен wp_location_meta_data_output_end. С его помощью можно будет добавить дополнительные данные перед закрытием мета бокса.
Выводим список мест
Необходимо создать функцию для создания HTML кода списка мест. Функция get_locations_output используется как виджетом, так и шорткодом для создания разметки. Так как функция многозадачная, в ней есть несколько важных действий. Разберем все по порядку.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
//главная функция отображения списка мест (используется шорткодами и виджетами) public function get_locations_output($arguments = ""){ //стандартные аргументы $default_args = array( 'location_id' => '', 'number_of_locations' => -1 ); //если переданы новые аргументы, обновляем if(!empty($arguments) && is_array($arguments)){ //проходимся в цикле по всем аргументам foreach($arguments as $arg_key => $arg_val){ //если аргумент есть в наших дефолтных, обновляем его значение if(array_key_exists($arg_key, $default_args)){ $default_args[$arg_key] = $arg_val; } } } //находим места $location_args = array( 'post_type' => 'wp_locations', 'posts_per_page'=> $default_args['number_of_locations'], 'post_status' => 'publish' ); //if we passed in a single location to display if(!empty($default_args['location_id'])){ $location_args['include'] = $default_args['location_id']; } //вывод $html = ''; $locations = get_posts($location_args); //Если места есть if($locations){ $html .= '<article class="location_list cf">'; //пробегаемся циклом по местам foreach($locations as $location){ $html .= '<section class="location">'; //собираем данные о местах $wp_location_id = $location->ID; $wp_location_title = get_the_title($wp_location_id); $wp_location_thumbnail = get_the_post_thumbnail($wp_location_id,'thumbnail'); $wp_location_content = apply_filters('the_content', $location->post_content); if(!empty($wp_location_content)){ $wp_location_content = strip_shortcodes(wp_trim_words($wp_location_content, 40, '...')); } $wp_location_permalink = get_permalink($wp_location_id); $wp_location_phone = get_post_meta($wp_location_id,'wp_location_phone',true); $wp_location_email = get_post_meta($wp_location_id,'wp_location_email',true); //применяем фильтр перед выводом основного контента //(позволяем сторонним плагинам и темам встраиваться в HTML для отображения данных) $html = apply_filters('wp_location_before_main_content', $html); //заголовок $html .= '<h2 class="title">'; $html .= '<a href="' . $wp_location_permalink . '" title="view location">'; $html .= $wp_location_title; $html .= '</a>'; $html .= '</h2>'; //изображение и контент if(!empty($wp_location_thumbnail) || !empty($wp_location_content)){ $html .= '<p class="image_content">'; if(!empty($wp_location_thumbnail)){ $html .= $wp_location_thumbnail; } if(!empty($wp_location_content)){ $html .= $wp_location_content; } $html .= '</p>'; } //телефон и почта if(!empty($wp_location_phone) || !empty($wp_location_email)){ $html .= '<p class="phone_email">'; if(!empty($wp_location_phone)){ $html .= '<b>Phone: </b>' . $wp_location_phone . '</br>'; } if(!empty($wp_location_email)){ $html .= '<b>Email: </b>' . $wp_location_email; } $html .= '</p>'; } // применяем фильтр после вывода основного контента //( позволяем сторонним плагинам и темам встраиваться в HTML для отображения данных) $html = apply_filters('wp_location_after_main_content', $html); //подробнее $html .= ‘<a class=”link” href=”’ . $wp_location_permalink . ‘” title=”view location”>View Location</a>’; $html .= ‘</section>’; } $html .= ‘</article>’; $html .= ‘<div class=”cf”></div>’; } return $html; } |
В функции есть необязательный аргумент arguments. Он используется из-за того, что у нас и виджеты и шорткоды передают свои варианты отображения (для точной настройки того, что будет возвращено).
В массив заносятся стандартные аргументы. Количество мест будет установлено в -1 (все места), а ID будет без значения (т.е. мы хотим показать список мест, а не конкретное место).
Проверяем переданную переменную $arguments на пустоту, и является ли она массивом. Пробегаемся по всем аргументам $arguments и сверяем их ключи на совпадение с нашим массивом $default_args. Если что-то совпало, обновляем массив $default_args.
Массив $default_args используется для создания запроса для get_posts(), который и будет искать все места (если создано всего одно место, его мы и получим).
Теперь переходим к созданию HTML. Создаем переменную $html, куда будем заносить разметку.
Собираем все данные о месте (заголовок, контент, изображение и т.д.) и подготавливаем их к выводу.
Вызываем фильтр wp_location_before_main_content на переменную $html. Так сторонние плагины и темы смогут добавлять дополнительный контент перед заголовком места. Крайне полезно, если мы заранее создали дополнительные поля в панели администратора, которые потом можно выводить.
В вывод добавляются заголовок, изображение, контент, телефон и почта (с условиями на проверку их существования).
Перед выводом кнопки Подробнее мы вызываем наш второй фильтр wp_location_after_main_content. С его помощью сторонние плагины и темы смогут добавлять дополнительный контент прямо перед кнопкой.
Добавляем кнопку Подробнее и возвращаем нашу переменную $html.
Сохранение дополнительных мета данных
При сохранении нового места необходимо запускать функцию, которая будет собирать и обновлять наши дополнительные мета поля (почта, телефон, адрес и т.д). Для этого мы создадим функцию save_location.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
//запускается при добавлении или редактировании места public function save_location($post_id){ //ищем nonce if(!isset($_POST['wp_location_nonce_field'])){ return $post_id; } //проверяем nonce if(!wp_verify_nonce($_POST['wp_location_nonce_field'], 'wp_location_nonce')){ return $post_id; } //ищем автосохранение if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE){ return $post_id; } //получаем поля телефон, почта, адрес $wp_location_phone = isset($_POST['wp_location_phone']) ? sanitize_text_field($_POST['wp_location_phone']) : ''; $wp_location_email = isset($_POST['wp_location_email']) ? sanitize_text_field($_POST['wp_location_email']) : ''; $wp_location_address = isset($_POST['wp_location_address']) ? sanitize_text_field($_POST['wp_location_address']) : ''; //обновляем поля телефон, почта, адрес update_post_meta($post_id, 'wp_location_phone', $wp_location_phone); update_post_meta($post_id, 'wp_location_email', $wp_location_email); update_post_meta($post_id, 'wp_location_address', $wp_location_address); //ищем рабочие чаты и обновляем их foreach($_POST as $key => $value){ //если нашли, обновляем if(preg_match('/^wp_location_trading_hours_/', $key)){ update_post_meta($post_id, $key, $value); } } //хук сохранения места используется для сохранения дополнительных полей //через 'wp_location_meta_data_output_end' или 'wp_location_meta_data_output_end' do_action('wp_location_admin_save',$post_id, $_POST); } |
Что мы делаем:
Сначала ищем поле nonce и проверяем его на существование (передается через мета бокс). Также проверяем автосохранение. После всех проверок едем дальше.
Собираем телефон, почту и адрес и очищаем данные с помощью функции sanitize_text_field(). Данные заносятся в переменные и позже используются в нашей функции update_post_meta() для сохранения данных о месте.
Так как наши рабочие часы динамические, то вытягивать и сохранять их мы вынуждены слегка по-другому. Мы не знаем, сколько будет значений, поэтому мы не можем вытягивать их из массива $_POST по имени. Нам придется пройтись по всему массиву и проверять ключи на совпадение с «wp_location_trading_hours_». Если совпадения есть, мы обновляем значения и сохраняем их в качестве мета данных.
Почти в конце вызываем экшен wp_location_admin_save. Экшен сохраняет ID текущего места в $post_id, чтобы сторонние функции могли вытягивать дополнительные данные из массива $_POST и сохранять их в месте.
Загружаем админ и публичные скрипты и стили
Нам необходимо загрузить дополнительные CSS файлы как для front-end, так и для back-end стороны нашего сайта. Для этого мы создадим две функции, которые будут загружать любые скрипты или файлы стилей.
Внутри CSS файлов будут стили полей мета бокса в панели администратора, а также немного стилей front-end’а. Плагин замечательно работает без CSS стилей. Если вам эта часть не интересна, ее можно пропустить. (Если вы пропускаете эту часть, не забудьте удалить вызов экшенов в конструкторе)
1 2 3 4 5 6 7 8 9 10 |
//подключаем скрипты и стили на стороне back-end’а public function enqueue_admin_scripts_and_styles(){ wp_enqueue_style('wp_location_admin_styles', plugin_dir_url(__FILE__) . '/css/wp_location_admin_styles.css'); } // подключаем скрипты и стили на стороне front-end’а public function enqueue_public_scripts_and_styles(){ wp_enqueue_style('wp_location_public_styles', plugin_dir_url(__FILE__). '/css/wp_location_public_styles.css'); } |
Шорткод
Теперь можно перейти к классу шорткода, который будет работать в паре с нашим основным классом. Шорткоды предоставляют администратору простой интерфейс отображения различных мест. Шорткоды будут настраиваемые, администратор сможет задавать конкретный филиал (место) по ID или же показывать все филиалы сразу. Шорткод на странице будет выглядеть примерно следующим образом:
Работать мы будем в файле wp_location_shortcode.php.
Запрет на прямой доступ
Как в случае с основным файлом PHP, тут мы тоже хотим закрыть прямой доступ. Скопируйте код ниже в верхушку файла:
1 |
defined( 'ABSPATH' ) or die( 'Nope, not accessing this' ); |
Класс wp_location_shortcode
Создадим оболочку класса. Этот класс не будет таким большим, как главный, но в нем будем пара функций, по которым мы вкратце пробежимся.
1 2 3 4 |
//задаем функционал шорткода для отображения мест class wp_location_shortcode{ } |
Функция __construct
Необходимо задать конструктор для того, чтобы через него добавлять наши экшены и фильтры. В этом классе нужна всего одна функция.
1 2 3 4 |
//запускается при создании экземпляра класса public function __construct(){ add_action('init', array($this,'register_location_shortcodes')); //шорткоды } |
Функция register_location_shortcodes
С помощью этой функции мы будем добавлять шорткод. Мы вызываем функцию add_shortcode для добавления нового шорткода wp_locations. Для вывода шорткода будем использовать функцию location_shortcode_output.
1 2 3 4 |
//шорткод мест public function register_location_shortcodes(){ add_shortcode('wp_locations', array($this,'location_shortcode_output')); } |
Вывод шорткода
Функция вывода вызывается из функции add_shortcode и нужна для формирования кода для вывода шорткода на экран.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//отображение шорткода public function location_shortcode_output($atts, $content = '', $tag){ //делаем класс wp_simple_locations глобальным global $wp_simple_locations; //создаем дефолтные аргументы $arguments = shortcode_atts(array( 'location_id' => '', 'number_of_locations' => -1) ,$atts,$tag); //для вывода используем главную функцию вывода из класс location $html = $wp_simple_locations->get_locations_output($arguments); return $html; } |
Что делает функция:
Функция принимает аргументы шорткода в виде переменной $atts, контент шорткода $content и назавние шорткода $tag. Эти переменные используются для формирования кода на вывод.
Создаем глобальную переменную $wp_simple_locations, чтобы получить доступ к главному классу (и всем его функциям).
Создаем дефолтный массив аргументов шорткода с помощью функции shortcode_atts().
Формируем код на вывод с помощью функции get_locations_output из объекта $wp_simple_locations. В функцию передаем наши аргументы, чтобы контент в шорткоде мог быть динамическим. К примеру, можно передать ID одного места, тогда вернется всего одно местоположение.
Возвращаем шорткод, который можно отобразить на любой странице или посте, куда вы его добавите.
Создаем объект wp_location_shortcode
В самом конце класса необходимо создать объект wp_location_shortcode. Таким образом будет активирован весь функционал класса.
1 |
$wp_location_shortcode = new wp_location_shortcode; |
Виджет
Закончим статью классом, работающим с виджетом. Мы добавим виджет, потому что почти все темы сейчас поддерживают виджеты. Виджеты дают администраторам простой и быстрый способ демонстрации чего-либо (в нашем случае это филиалы компании). Откроем файл wp_location_widget.php и начнем.
Запрет прямого доступа
И опять мы запрещаем прямой доступ к PHP файлу с помощью следующей строки:
1 |
defined( 'ABSPATH' ) or die( 'Nope, not accessing this' ); |
Класс wp_location_widget
Создадим базовую структуру класса wp_location_widget. Класс похож на предыдущие; но в этот раз мы расширим уже существующий класс WP_widget.
1 2 3 4 |
//главный виджет для отображения филиалов компании class wp_location_widget extends WP_widget{ } |
Функция __construct
В функции _construct мы определим наши базовые стили виджета. А сделаем мы это, перегрузив родительский конструктор и передав в него наши значения. Нам необходимо задать ID, название и описание виджета. Также мы прицепим нашу функцию register_wp_location_widgets к хуку widgets_init, чтобы зарегистрировать наш виджет.
1 2 3 4 5 6 7 8 9 10 |
//инициализация значений виджета public function __construct(){ //задаем базовые значения виджета (переписывая родительские) parent::__construct( 'wp_location_widget', 'WP Location Widget', array('description' => 'A widget that displays your locations') ); add_action('widgets_init',array($this,'register_wp_location_widgets')); } |
Создаем интерфейс виджета в панели администратора
Интерфейс это то, через что администратор будет взаимодействовать с нашим виджетом. Мы хотим добавить несколько вариантов, чтобы был выбор, по сколько мест отображать в виджете, а также возможность показывать всего одно место.
Функция form() унаследована от родительского класса WP_widget, она будет автоматически вызываться, когда мы будем заходить на экран виджета в панели администратора.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
//код back-end’а виджета //$instance – сохраняет значения формы public function form($instance){ //собираем переменные $location_id = (isset($instance['location_id']) ? $instance['location_id'] : 'default'); $number_of_locations = (isset($instance['number_of_locations']) ? $instance['number_of_locations'] : 5); ?> <p>Select your options below</p> <p> <label for="<?php echo $this->get_field_name('location_id'); ?>">Location to display</label> <select class="widefat" name="<?php echo $this->get_field_name('location_id'); ?>" id="<?php echo $this->get_field_id('location_id'); ?>" value="<?php echo $location_id; ?>"> <option value="default">All Locations</option> <?php $args = array( 'posts_per_page' => -1, 'post_type' => 'wp_locations' ); $locations = get_posts($args); if($locations){ foreach($locations as $location){ if($location->ID == $location_id){ echo '<option selected value="' . $location->ID . '">' . get_the_title($location->ID) . '</option>'; }else{ echo '<option value="' . $location->ID . '">' . get_the_title($location->ID) . '</option>'; } } } ?> </select> </p> <p> <small>If you want to display multiple locations select how many below</small><br/> <label for="<?php echo $this->get_field_id('number_of_locations'); ?>">Number of Locations</label> <select class="widefat" name="<?php echo $this->get_field_name('number_of_locations'); ?>" id="<?php echo $this->get_field_id('number_of_locations'); ?>" value="<?php echo $number_of_locations; ?>"> <option value="default" <?php if($number_of_locations == 'default'){ echo 'selected';}?>>All Locations</option> <option value="1" <?php if($number_of_locations == '1'){ echo 'selected';}?>>1</option> <option value="2" <?php if($number_of_locations == '2'){ echo 'selected';}?>>2</option> <option value="3" <?php if($number_of_locations == '3'){ echo 'selected';}?>>3</option> <option value="4" <?php if($number_of_locations == '4'){ echo 'selected';}?>>4</option> <option value="5" <?php if($number_of_locations == '5'){ echo 'selected';}?>>5</option> <option value="10" <?php if($number_of_locations == '10'){ echo 'selected';}?>>10</option> </select> </p> <?php } |
Что тут происходит:
Сперва мы задаем ID и количество мест. Проверяем переменную $instance на наличие этих значений (существуют ли они). Если значения есть, извлекаем их. Если их нет, просто передаем стандартные значения (устанавливаем количество мест на 5 и передаем ID по умолчанию).
Создаем лейбл и форму для показа мест в панели администратора. С помощью функции get_posts() вытягиваем все места и показываем их. Каждое место мы проверяем на совпадение с сохраненным ID (если он был задан).
Создаем список select для отображения всех наших мест. И опять проверяем все варианты на совпадение с переданным значением.
Обновление виджета и сохранение вариантов
Для сохранения значений формы необходимо вызывать функцию обновления виджета. Функция update() унаследована от класса WP_widget. Нам осталось только указать, как сохранять значения.
1 2 3 4 5 6 7 8 9 10 11 |
//обновление виджета //$new_instance – новые значения, $old_instance – старые сохраненные значения public function update($new_instance, $old_instance){ $instance = array(); $instance['location_id'] = $new_instance['location_id']; $instance['number_of_locations'] = $new_instance['number_of_locations']; return $instance; } |
У нас есть две переменные $new_instance и $old_instance. В $new_instance хранятся текущие значения формы, а в $old_instance хранятся старые значения. Нам необходимо создать новый массив для хранения извлеченных значений для последующего их возврата.
Отображение виджета на стороне front-end’а
Функция widget() унаследована из класса WP_widget, с ее помощью виджет показывается на стороне front-end’а. Для формирования кода на вывод используется функция отображения из класса wp_simple_locations.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
//публичное отображение виджета //$args – аргументы виджета, $instance – сохраненные значения public function widget( $args, $instance ) { //получаем доступ к классу wp_simple_location class (с его помощью будем выводить виджет на экран) global $wp_simple_locations; //передаем все имеющиеся аргументы из виджета $arguments = array(); //если место задано //если задано конкретное место if($instance['location_id'] != 'default'){ $arguments['location_id'] = $instance['location_id']; } //если задано несколько мест if($instance['number_of_locations'] != 'default'){ $arguments['number_of_locations'] = $instance['number_of_locations']; } //вывод $html = ''; $html .= $args['before_widget']; $html .= $args['before_title']; $html .= 'Locations'; $html .= $args['after_title']; //используем главную функцию вывода из класса location $html .= $wp_simple_locations->get_locations_output($arguments); $html .= $args['after_widget']; echo $html; } |
Что мы тут делаем:
Создаем глобальный объект $wp_simple_locations, нам нужна его функция вывода на экран.
Создаем пустой массив аргументов и проверяем, заданы ли аргументы для виджета (количество мест, к примеру).
Начинаем формировать код на вывод и создаем переменную $html. Вызываем функцию get_locations_output(), прописанную в объекте $wp_simple_locations, и передаем в нее аргументы (нам вернется весь HTML код).
Выводим весь код из переменной $html на экран.
Регистрация виджета
Функция ниже нужна для регистрации виджета в WordPress. Необходимо вызвать функцию register_widget() и передать в нее название класса в качестве значения.
1 2 3 4 |
//регистрация виджета public function register_wp_location_widgets(){ register_widget('wp_location_widget'); } |
Надеюсь, вам понравился реальный пример разработки плагина для WordPress с нуля. Следите за моими статьями.
Автор: Simon Codrington
Источник: //www.sitepoint.com/
Редакция: Команда webformyself.