Пытаясь реализовать удобный функционал по созданию меток на интерактивной карте я пришел к следующей системе. Заводим произвольный тип записи и таксономии чтобы структурировать и удобно манипулировать городами и точками.
Основа
В основе решения находиться плагин Oi Yandex.Maps for WordPress (хотя я использую его только частично), который в свою очередь основан на Яндекс Api. т.е. плагин необходимо установить.
Создаем пользовательский тип записи city (название не вполне соответствует, т.к. это будут точки. конкретное место в городе):
// Добавляем произвольный тип записей
add_action( 'init', 'register_post_type_city' ); // Использовать функцию только внутри хука init
function register_post_type_city() {
$labels = array(
'name' => 'Где купить',
'singular_name' => 'City', // админ панель Добавить->Функцию
'add_new' => 'Добавить точку',
'add_new_item' => 'Добавить новую точку', // заголовок тега <title>
'edit_item' => 'Редактировать точку',
'new_item' => 'Новая точка',
'all_items' => 'Все точки',
'view_item' => 'Просмотр страницы точки на сайте',
'search_items' => 'Искать точку',
'not_found' => 'Точка не найдена.',
'not_found_in_trash' => 'В корзине нет точки.',
'menu_name' => 'Где купить' // ссылка в меню в админке
);
$args = array(
'labels' => $labels,
'public' => true,
'show_ui' => true, // показывать интерфейс в админке
'has_archive' => true, // показывать страницу архива данного типа записей
'menu_icon' => 'dashicons-location-alt', // иконка dashicons в меню
'menu_position' => 20, // порядок в меню
'supports' => array( 'title', 'editor', 'comments', 'author', 'thumbnail'),
//'taxonomies' => array('cities') // добавляем поддержку стандартных таксономий 'post_tag', 'category', либо пользовательскую 'album' (через запятую)
);
register_post_type('city', $args);
}
// Тексты уведомлений для типа постов
add_filter( 'post_updated_messages', 'true_post_type_messages_city' );
function true_post_type_messages_city( $messages ) {
global $post, $post_ID;
$messages['city'] = array( // city - название созданного нами типа записей
0 => '', // Данный индекс не используется.
1 => sprintf( 'Точка обновлена. Просмотр', esc_url( get_permalink($post_ID) ) ),
2 => 'Точка обновлёна.',
3 => 'Точка удалёна.',
4 => 'Точка обновлена.',
5 => isset($_GET['revision']) ? sprintf( 'Точка восстановлена из редакции: %s', wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
6 => sprintf( 'Точка опубликована на сайте. Просмотр', esc_url( get_permalink($post_ID) ) ),
7 => 'Точка сохранена.',
8 => sprintf( 'Отправлена на проверку. Просмотр', esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
9 => sprintf( 'Запланирована на публикацию: %1$s. Просмотр', date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
10 => sprintf( 'Черновик обновлён. Просмотр', esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
);
return $messages;
}
// Произвольные поля для пользовательского типа записей
function true_custom_fields_city() {
add_post_type_support( 'city', 'custom-fields'); // в качестве первого параметра укажите название типа поста
}
add_action('init', 'true_custom_fields_city');
//Создаем пользовательскую таксономию (элементами этой таксономии будут города)
// Пользовательская таксономия Город
function add_new_taxonomies() {
register_taxonomy('cities', // создаем функцию с произвольным именем и вставляем в неё register_taxonomy()
array('city'),
array(
'hierarchical' => false, // true - по типу рубрик, false - по типу меток, по умолчанию - false
'labels' => array(
/* ярлыки, нужные при создании UI, можете не писать ничего, тогда будут использованы ярлыки по умолчанию */
'name' => 'Город',
'singular_name' => 'City',
'search_items' => 'Найти город',
'popular_items' => 'Популярные города',
'all_items' => 'Все города',
'parent_item' => null,
'parent_item_colon' => null,
'edit_item' => 'Редактировать город',
'update_item' => 'Обновить город',
'add_new_item' => 'Добавить новый город',
'new_item_name' => 'Название нового города',
'separate_items_with_commas' => 'Разделяйте города запятыми',
'add_or_remove_items' => 'Добавить или удалить город',
'choose_from_most_used' => 'Выбрать из наиболее часто используемых городов',
'menu_name' => 'Города'
),
'public' => true, // каждый может использовать таксономию, либо только администраторы, по умолчанию - true
'show_in_nav_menus' => true, // добавить на страницу создания меню
'show_ui' => true, // добавить интерфейс создания и редактирования
'show_tagcloud' => true, // нужно ли разрешить облако тегов для этой таксономии
'update_count_callback' => '_update_post_term_count', // callback-функция для обновления счетчика $object_type
'query_var' => true, // разрешено ли использование query_var, также можно указать строку, которая будет использоваться в качестве него, по умолчанию - имя таксономии
'rewrite' => array( // настройки URL пермалинков
'slug' => 'cities', // ярлык
'hierarchical' => false // разрешить вложенность
),
)
);
}
add_action( 'init', 'add_new_taxonomies', 0 );
Города точкам назначать будем как метки.
Произвольные поля
Задаем для записей типа city поля Координаты точки, Название точки, Контакты точки:
// Поле Координаты точки
add_action('add_meta_boxes', 'coordinati_tochka_box');
function coordinati_tochka_box() { add_meta_box('material_box', 'Координаты', 'coordinati_tochka_box_func', 'city', 'normal'); }
function coordinati_tochka_box_func($post){
$coordinati_tochka = get_post_meta($post->ID, 'coordinati_tochka', 1);
?>
<label><input style="width:100%;" type="text" name="extra[coordinati_tochka]" value="<?php if($coordinati_tochka){echo $coordinati_tochka;}?>" /></label>
<input type="hidden" name="atb_nonce" value="<?php echo wp_create_nonce(__FILE__); ?>" />
<?php
}
add_action('save_post', 'coordinati_tochka_box_update');
function coordinati_tochka_box_update($post_id){
if (!wp_verify_nonce($_POST['atb_nonce'], __FILE__)) return false; // Проверка, что сохраняется именно нужная нам страница.
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return false; // Проверка, что это не автосохранение
if (!current_user_can('edit_post', $post_id)) return false; // Проверка, что пользователь может изменять этот пост
if(!isset($_POST['extra']) ) return false; // Проверка, что нам пришли все поля
foreach($_POST['extra'] as $key=>$value){ // Циклом добавляем поля. Можно и в ручную это делать... Но так проще добавить новое поле
if(empty($value) AND $value != 0){ // Если значение пустое и не равно 0
delete_post_meta($post_id, $key); // Удаляем это поле из мета-данных
continue; // Продолжаем
}
update_post_meta($post_id, $key, $value); // Обновляем или добавляем мета-данные
}
return $post_id;
}
// Поле Название точки
add_action('add_meta_boxes', 'name_tochka_box');
function name_tochka_box() { add_meta_box('material_box_2', 'Название точки', 'name_tochka_box_func', 'city', 'normal'); }
function name_tochka_box_func($post){
$name_tochka = get_post_meta($post->ID, 'name_tochka', 1);
?>
<label><input style="width:100%;" type="text" name="extra[name_tochka]" value="<?php if($name_tochka){echo $name_tochka;}?>" /></label>
<input type="hidden" name="atb_nonce" value="<?php echo wp_create_nonce(__FILE__); ?>" />
<?php
}
add_action('save_post', 'name_tochka_box_update');
function name_tochka_box_update($post_id){
if (!wp_verify_nonce($_POST['atb_nonce'], __FILE__)) return false; // Проверка, что сохраняется именно нужная нам страница.
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return false; // Проверка, что это не автосохранение
if (!current_user_can('edit_post', $post_id)) return false; // Проверка, что пользователь может изменять этот пост
if(!isset($_POST['extra']) ) return false; // Проверка, что нам пришли все поля
foreach($_POST['extra'] as $key=>$value){ // Циклом добавляем поля. Можно и в ручную это делать... Но так проще добавить новое поле
if(empty($value) AND $value != 0){ // Если значение пустое и не равно 0
delete_post_meta($post_id, $key); // Удаляем это поле из мета-данных
continue; // Продолжаем
}
update_post_meta($post_id, $key, $value); // Обновляем или добавляем мета-данные
}
return $post_id;
}
// Поле Контакты точки
add_action('add_meta_boxes', 'contacts_tochka_box');
function contacts_tochka_box() { add_meta_box('material_box_3', 'Контакты точки', 'contacts_tochka_box_func', 'city', 'normal'); }
function contacts_tochka_box_func($post){
$contacts_tochka = get_post_meta($post->ID, 'contacts_tochka', 1);
?>
<label><input style="width:100%;" type="text" name="extra[contacts_tochka]" value="<?php if($contacts_tochka){echo $contacts_tochka;}?>" /></label>
<input type="hidden" name="atb_nonce" value="<?php echo wp_create_nonce(__FILE__); ?>" />
<?php
}
add_action('save_post', 'contacts_tochka_box_update');
function contacts_tochka_box_update($post_id){
if (!wp_verify_nonce($_POST['atb_nonce'], __FILE__)) return false; // Проверка, что сохраняется именно нужная нам страница.
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return false; // Проверка, что это не автосохранение
if (!current_user_can('edit_post', $post_id)) return false; // Проверка, что пользователь может изменять этот пост
if(!isset($_POST['extra']) ) return false; // Проверка, что нам пришли все поля
foreach($_POST['extra'] as $key=>$value){ // Циклом добавляем поля. Можно и в ручную это делать... Но так проще добавить новое поле
if(empty($value) AND $value != 0){ // Если значение пустое и не равно 0
delete_post_meta($post_id, $key); // Удаляем это поле из мета-данных
continue; // Продолжаем
}
update_post_meta($post_id, $key, $value); // Обновляем или добавляем мета-данные
}
return $post_id;
}
В последствии в поле Координаты точки мы будем прописывать реальные координаты точки, беря их из карт Яндекс. Поля Название точки и Контакты точки нужны для сопроводительного текста при нажатии на точку, или если точки выводить в виде списка.
Зададим произвольное поле и для таксономии с городами:
// Создание метаполя Координаты для города
function pippin_taxonomy_add_new_meta_field() { // this will add the custom meta field to the add new term page
?>
<div class="form-field">
<label for="term_meta[custom_term_meta]"><?php _e( 'Example meta field', 'pippin' ); ?></label>
<input type="text" name="term_meta[custom_term_meta]" id="term_meta[custom_term_meta]" value="">
<p class="description"><?php _e( 'Enter a value for this field','pippin' ); ?></p>
</div>
<?php
}
add_action( 'cities_add_form_fields', 'pippin_taxonomy_add_new_meta_field', 10, 2 );
// Редактирование мета поля
function pippin_taxonomy_edit_meta_field($term) {
$t_id = $term->term_id; // put the term ID into a variable
$term_meta = get_option( "taxonomy_$t_id" ); // retrieve the existing value(s) for this meta field. This returns an array
?>
<tr class="form-field">
<th scope="row" valign="top"><label for="term_meta[custom_term_meta]"><?php _e( 'Example meta field', 'pippin' ); ?></label></th>
<td>
<input type="text" name="term_meta[custom_term_meta]" id="term_meta[custom_term_meta]" value="<?php echo esc_attr( $term_meta['custom_term_meta'] ) ? esc_attr( $term_meta['custom_term_meta'] ) : ''; ?>">
<p class="description"><?php _e( 'Enter a value for this field','pippin' ); ?></p>
</td>
</tr>
<?php
}
add_action( 'cities_edit_form_fields', 'pippin_taxonomy_edit_meta_field', 10, 2 );
// Сохранение метаполя
function save_taxonomy_custom_meta( $term_id ) {
if ( isset( $_POST['term_meta'] ) ) {
$t_id = $term_id;
$term_meta = get_option( "taxonomy_$t_id" );
$cat_keys = array_keys( $_POST['term_meta'] );
foreach ( $cat_keys as $key ) {
if ( isset ( $_POST['term_meta'][$key] ) ) { $term_meta[$key] = $_POST['term_meta'][$key]; }
}
update_option( "taxonomy_$t_id", $term_meta ); // Save the option array.
}
}
add_action( 'edited_cities', 'save_taxonomy_custom_meta', 10, 2 );
add_action( 'create_cities', 'save_taxonomy_custom_meta', 10, 2 );
Это поле нужно чтобы вписывать координаты городов, которые придется предварительно уточнять в Яндекс картах.
Шаблоны вывода
Шаблон archive-city.php — здесь будем выводить список всех городов, причем размечая их по алфавиту. Для меню шаблон доступен по ссылке /city/
<?php $terms = get_terms( 'cities'); ?>
<script type="text/javascript" src="https://api-maps.yandex.ru/2.1/?lang=ru_RU"></script>
<style>.YMaps {position: relative; height: 400px; width:100%;}</style>
<div id="YMaps_0" class="YMaps"></div>
<script type="text/javascript">
ymaps.ready(init);
function init () {
var myMap = new ymaps.Map("YMaps_0", {
center: [47.413167,40.532044],
zoom: 7
});
myMap.controls.add("routeEditor");
<?php foreach( $terms as $term ): ?>
<?php $term_meta = get_option( "taxonomy_$term->term_id" );
$term_link = get_term_link($term->term_id, 'cities'); ?>
myPlacemark_1 = new ymaps.Placemark([<?php echo esc_attr( $term_meta['custom_term_meta'] ) ? esc_attr( $term_meta['custom_term_meta'] ) : ''; ?>],
{balloonContentHeader: "<a href='<?php echo $term_link; ?>'><?php echo $term->name; ?></a>",},
{preset: "islands#redDotIcon"}
);
myMap.geoObjects.add(myPlacemark_1);
<?php endforeach; ?>
}
</script>
<h1 id="title"><i class="fa fa-map-marker" aria-hidden="true"></i> Где купить</h1>
<div id="all-city">
<?php global $query_string;
parse_str($query_string, $args); // добавляем базовые параметры в массив $args
$args['posts_per_page'] = -1; // добавляем/заменяем параметр в массиве
query_posts( $args ); ?>
<?php echo '<div class="city-letter">';
foreach( $terms as $k => $term ){
$fl = get_first_letter( $term->name ); // первая буква
$prev_fl = isset( $terms[ ($k-1) ] ) ? get_first_letter( $terms[ ($k-1) ]->name ) : '';
if( $prev_fl !== $fl )
echo '</div><div class="city-letter"><h3>'.$fl.'</h3>'; // Буква
printf ( '<a href="%s">%s</a><br>' , get_term_link($term->term_id, 'cities'), $term->name ); // Выводим название записи в виде ссылки на неё
}
echo '</div>';
wp_reset_postdata(); ?>
<?php // Функция возвращает первую букву переданного в неё слова
function get_first_letter( $str ){ return mb_substr($str, 0, 1, 'utf-8'); } ?>
</div>
Выглядеть это должно примерно так:
Шаблон taxonomy-cities.php будет выводить элементы пользовательской таксономии (отдельный город с точками)
<?php $term_slug = get_query_var( 'cities' );
$term = get_term_by('slug', $term_slug, 'cities');
$term_meta = get_option( "taxonomy_$term->term_id" ); ?>
<script type="text/javascript" src="https://api-maps.yandex.ru/2.1/?lang=ru_RU"></script>
<style>.YMaps {position: relative; width:100%; height:650px;}}</style>
<div id="YMaps_0" class="YMaps"></div>
<script type="text/javascript">
ymaps.ready(init);
function init () {
var myMap = new ymaps.Map("YMaps_0", {
center: [<?php echo esc_attr( $term_meta['custom_term_meta'] ) ? esc_attr( $term_meta['custom_term_meta'] ) : ''; ?>], //найти какая функция отвечает за определение центра, а пока сделать полем
zoom: 11
});
myMap.controls.add("routeEditor");
<?php if ( have_posts() ) : ?>
<?php while ( have_posts() ) : the_post(); ?>
myPlacemark_1 = new ymaps.Placemark([<?php echo get_post_meta($post->ID, 'coordinati_tochka', 1); ?>], //научиться налету конвертировать координаты
{balloonContentHeader: "<?php echo get_post_meta($post->ID, 'name_tochka', 1); ?>",
balloonContentBody: "<?php echo get_post_meta($post->ID, 'contacts_tochka', 1); ?>"},
{preset: "islands#redDotIcon"}
);
myMap.geoObjects.add(myPlacemark_1);
<?php endwhile; ?>
<?php endif; ?>
}
</script>
<h1 id="title"><?php echo $term->name; ?></h1>
<?php if ( have_posts() ) : ?><ul>
<?php while ( have_posts() ) : the_post(); ?>
<li><strong><?php echo get_post_meta($post->ID, 'name_tochka', 1); ?></strong> <?php the_title(); ?> <?php echo get_post_meta($post->ID, 'contacts_tochka', 1); ?></li>
<?php endwhile; ?></ul>
<?php endif; ?>
В этом шаблоне мне пришлось оказаться от хлебных крошек от Kama (получалась несуразица) и прописать свои:
<div id="path"><div class="kama_breadcrumbs" itemscope="" itemtype="http://schema.org/BreadcrumbList">
<span itemprop="itemListElement" itemscope="" itemtype="http://schema.org/ListItem"><a href="https://site.ru" itemprop="item"><span itemprop="name">Главная</span></a></span> /
<a href="/city/" itemprop="item"><span itemprop="name">Где купить</span></a> / <span class="current"><?php echo $term->name; ?></span></div>
</div>
В итоге должно получиться примерно так:
Информация в balloonContentBody
В всплывающий блок balloonContentBody можно вставлять любую верстку html и контент. Пример:
balloonContentBody: "<img src='/wp-content/uploads/2019/09/image.jpg'><br><p>г. Ростов-на-Дону, пр. Космонавтов, 39</p>"
[site-socialshare]