← Назад в блог

WordPress модуль для Яндекс.Карт: проекты на карте с ACF и кластеризацией

Практический модуль для WordPress: кастомный тип записи для проектов, ACF поля для координат, интеграция с Яндекс.Картами, кластеризация меток и адаптивный дизайн. Готовое решение для отображения реализованных проектов на карте.

WordPress модуль для Яндекс.Карт: проекты на карте с ACF и кластеризацией

Требования

  • WordPress 5.0+
  • PHP 7.2+
  • ACF (Advanced Custom Fields) плагин
  • API ключ Яндекс.Карт

WordPress модуль для Яндекс.Карт: проекты на карте с ACF и кластеризацией

Если тебе нужно показать реализованные проекты на карте — например, установленное оборудование в разных городах — обычные решения либо слишком сложные, либо не дают нужной гибкости. Модуль проектов для WordPress решает эту задачу просто: кастомный тип записи для проектов, ACF поля для координат и адресов, интеграция с Яндекс.Картами с кластеризацией меток. Всё готово к использованию, легко переносится в другой сайт. (GitHub)

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


Задача: что нужно было решить

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

  • координаты (широта/долгота)
  • адрес
  • тип и модель оборудования
  • описание проекта

При этом:

  • проекты должны управляться через админку WordPress
  • карта должна автоматически группировать близкие метки (кластеризация)
  • решение должно быть переносимым в другой сайт
  • если проектов нет — показывать тестовые данные для демонстрации

Архитектура решения

Модуль состоит из четырёх основных файлов:

  1. projects.php — регистрация кастомного типа записи и таксономии
  2. projects-acf.php — ACF поля для координат и данных проектов
  3. projects-map.php — функции для работы с картой, подключение скриптов
  4. projects-loader.php — главный файл, который подключает всё остальное

Почему именно такая структура

Разделение на файлы делает модуль:

  • понятным — каждый файл отвечает за свою задачу
  • переносимым — можно скопировать папку и подключить в другом сайте
  • расширяемым — легко добавить новые поля или функции

Шаг 1: Регистрация кастомного типа записи

Файл: projects.php

<?php
add_action('init', function(){
    register_post_type('projects', array(
        'labels' => array(
            'name'               => 'Проекты',
            'singular_name'      => 'Проект',
            'add_new'            => 'Добавить новый',
            'add_new_item'       => 'Добавить новый проект',
            'edit_item'          => 'Редактировать проект',
            'menu_name'          => 'Проекты',
        ),
        'public'             => false, // Проекты только для карты
        'publicly_queryable' => false, // Нельзя открыть отдельную страницу
        'show_in_nav_menus'  => false, // Не показывать в меню
        'supports'           => ['title', 'editor', 'thumbnail'],
        'show_ui'            => true, // Показывать в админке
        'has_archive'        => false, // Нет архива
        'show_in_rest'       => true,
        'menu_icon'          => 'dashicons-location-alt'
    ));
});

Важно: 'public' => false означает, что проекты не имеют публичных URL. Они используются только для карты, что идеально для локаций без отдельных страниц.

Регистрация таксономии для типов оборудования

add_action('init', function(){
    register_taxonomy('project-equipment', ['projects'], [
        'label'            => 'Тип оборудования',
        'public'           => false, // Таксономия не публичная
        'show_ui'          => true,
        'show_in_rest'     => true,
        'hierarchical'     => true,
        'show_admin_column' => true
    ]);
});

Шаг 2: ACF поля для координат и данных

Файл: projects-acf.php

ACF поля создаются программно через acf_add_local_field_group(). Это удобно, потому что поля автоматически создаются при активации темы — не нужно настраивать их вручную.

<?php
if(function_exists('acf_add_local_field_group')):
    acf_add_local_field_group(array(
        'key' => 'group_projects_fields',
        'title' => 'Поля проекта',
        'fields' => array(
            array(
                'key' => 'field_project_latitude',
                'label' => 'Широта (Latitude)',
                'name' => 'project_latitude',
                'type' => 'text',
                'required' => 1,
                'placeholder' => '55.751574',
            ),
            array(
                'key' => 'field_project_longitude',
                'label' => 'Долгота (Longitude)',
                'name' => 'project_longitude',
                'type' => 'text',
                'required' => 1,
                'placeholder' => '37.573856',
            ),
            array(
                'key' => 'field_project_address',
                'label' => 'Адрес',
                'name' => 'project_address',
                'type' => 'text',
            ),
            array(
                'key' => 'field_project_equipment_type',
                'label' => 'Тип оборудования',
                'name' => 'project_equipment_type',
                'type' => 'text',
            ),
            array(
                'key' => 'field_project_equipment_model',
                'label' => 'Модель оборудования',
                'name' => 'project_equipment_model',
                'type' => 'text',
            ),
        ),
        'location' => array(
            array(
                array(
                    'param' => 'post_type',
                    'operator' => '==',
                    'value' => 'projects',
                ),
            ),
        ),
    ));
endif;

Почему программное создание полей:

  • Поля создаются автоматически при активации темы
  • Не нужно настраивать их вручную в админке
  • Легко перенести модуль в другой сайт

Шаг 3: Функция получения проектов для карты

Файл: projects.php

/**
 * Получить все проекты для карты
 * @return array Массив проектов с координатами
 */
function get_projects_for_map() {
    $projects = get_posts(array(
        'post_type'      => 'projects',
        'posts_per_page' => -1,
        'post_status'    => 'publish'
    ));

    $projects_data = array();

    foreach ($projects as $project) {
        $latitude = get_field('project_latitude', $project->ID);
        $longitude = get_field('project_longitude', $project->ID);
        $address = get_field('project_address', $project->ID);
        $equipment_type = get_field('project_equipment_type', $project->ID);
        $equipment_model = get_field('project_equipment_model', $project->ID);

        // Пропускаем проекты без координат
        if (empty($latitude) || empty($longitude)) {
            continue;
        }

        $projects_data[] = array(
            'id'             => $project->ID,
            'title'          => get_the_title($project->ID),
            'latitude'       => floatval($latitude),
            'longitude'      => floatval($longitude),
            'address'        => $address ?: '',
            'equipment_type' => $equipment_type ?: '',
            'equipment_model' => $equipment_model ?: '',
            'description'    => wp_strip_all_tags(get_the_excerpt($project->ID))
        );
    }

    return $projects_data;
}

Что делает функция:

  • Получает все опубликованные проекты
  • Извлекает ACF поля (координаты, адрес, оборудование)
  • Пропускает проекты без координат
  • Возвращает массив, готовый для передачи в JavaScript

Шаг 4: Подключение Яндекс.Карт и скриптов

Файл: projects-map.php

/**
 * Подключение скриптов и стилей для карты проектов
 */
function enqueue_projects_map_assets() {
    // Подключаем только на странице с шаблоном карты
    if (is_page_template('template-projects-map.php')) {
        // Получаем API ключ из настроек ACF
        $yandex_api_key = get_field('yandex_maps_api_key', 'options') ?: '';
        if (empty($yandex_api_key)) {
            $yandex_api_key = 'YOUR_API_KEY'; // Замените на ваш API ключ
        }

        // Регистрируем API Яндекс.Карт
        wp_register_script(
            'yandex-maps-api',
            'https://api-maps.yandex.ru/2.1/?apikey=' . esc_attr($yandex_api_key) . '&lang=ru_RU',
            array(),
            '2.1',
            false
        );

        // Получаем данные проектов
        $projects_data = get_projects_map_data();

        // Передаем данные в JavaScript
        wp_localize_script('yandex-maps-api', 'projectsMapData', array(
            'projects' => $projects_data
        ));

        // Подключаем скрипты
        wp_enqueue_script('yandex-maps-api');
        wp_enqueue_script(
            'projects-map-js',
            get_template_directory_uri() . '/inc/modules/projects/assets/js/projects-map.js',
            array('yandex-maps-api'),
            '1.0.0',
            true
        );

        // Подключаем стили
        wp_enqueue_style(
            'projects-map-css',
            get_template_directory_uri() . '/inc/modules/projects/assets/css/projects-map.css',
            array(),
            '1.0.0'
        );
    }
}
add_action('wp_enqueue_scripts', 'enqueue_projects_map_assets');

Важные моменты:

  • Скрипты подключаются только на странице с шаблоном карты
  • API ключ берётся из настроек ACF (можно настроить в админке)
  • Данные проектов передаются в JavaScript через wp_localize_script()

Шаг 5: JavaScript для инициализации карты

Файл: assets/js/projects-map.js

(function () {
  "use strict";

  document.addEventListener("DOMContentLoaded", function () {
    if (
      typeof ymaps !== "undefined" &&
      typeof projectsMapData !== "undefined"
    ) {
      ymaps.ready(function () {
        var projectsData = projectsMapData.projects || [];

        // Создаем карту
        var myMap = new ymaps.Map("yandex-map", {
          center: [55.751574, 37.573856], // Москва по умолчанию
          zoom: 5,
          controls: ["zoomControl", "typeSelector", "fullscreenControl"],
        });

        // Создаем кластеры для меток
        var clusterer = new ymaps.Clusterer({
          preset: "islands#invertedBlueClusterIcons",
          groupByCoordinates: false,
          clusterDisableClickZoom: true,
          clusterHideIconOnBalloonOpen: false,
          geoObjectHideIconOnBalloonOpen: false,
        });

        // Добавляем метки для каждого проекта
        projectsData.forEach(function (project) {
          var placemark = new ymaps.Placemark(
            [project.latitude, project.longitude],
            {
              balloonContentHeader:
                "<strong>" + escapeHtml(project.title) + "</strong>",
              balloonContentBody:
                (project.equipment_type
                  ? "<p><strong>Тип оборудования:</strong> " +
                    escapeHtml(project.equipment_type) +
                    "</p>"
                  : "") +
                (project.equipment_model
                  ? "<p><strong>Модель:</strong> " +
                    escapeHtml(project.equipment_model) +
                    "</p>"
                  : "") +
                (project.address
                  ? "<p><strong>Адрес:</strong> " +
                    escapeHtml(project.address) +
                    "</p>"
                  : "") +
                (project.description
                  ? "<p>" + escapeHtml(project.description) + "</p>"
                  : ""),
              hintContent: escapeHtml(project.title),
            },
            {
              preset: "islands#blueIcon",
            }
          );

          clusterer.add(placemark);
        });

        myMap.geoObjects.add(clusterer);

        // Устанавливаем границы карты, чтобы показать все метки
        if (projectsData && projectsData.length > 0) {
          myMap.setBounds(clusterer.getBounds(), {
            checkZoomRange: true,
            duration: 300,
          });
        }
      });
    }
  });

  // Функция для экранирования HTML (защита от XSS)
  function escapeHtml(text) {
    if (!text) return "";
    var map = {
      "&": "&amp;",
      "<": "&lt;",
      ">": "&gt;",
      '"': "&quot;",
      "'": "&#039;",
    };
    return text.replace(/[&<>"']/g, function (m) {
      return map[m];
    });
  }
})();

Что делает код:

  • Инициализирует карту Яндекс.Карт
  • Создаёт кластеры для группировки близких меток
  • Добавляет метки для каждого проекта с балунами
  • Автоматически подстраивает границы карты под все метки
  • Экранирует HTML для защиты от XSS

Шаг 6: Fallback данные для демонстрации

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

Файл: projects-map.php

/**
 * Получить данные проектов для карты (с fallback на тестовые данные)
 */
function get_projects_map_data() {
    // Пытаемся получить данные из базы
    if (function_exists('get_projects_for_map')) {
        $projects_data = get_projects_for_map();
        if (!empty($projects_data)) {
            return $projects_data;
        }
    }

    // Если проектов нет, возвращаем тестовые данные
    return array(
        array(
            'id' => 1,
            'title' => 'Крематор КД-300',
            'latitude' => 55.751574,
            'longitude' => 37.573856,
            'address' => 'г. Москва, ул. Красная площадь, д. 1',
            'equipment_type' => 'Крематор',
            'equipment_model' => 'КД-300',
            'description' => 'Установлен крематор КД-300 для утилизации отходов.'
        ),
        // ... другие тестовые проекты
    );
}

Зачем это нужно:

  • Можно показать карту клиенту до добавления реальных проектов
  • Удобно для тестирования и разработки
  • Не нужно создавать тестовые записи в базе

Использование: создание страницы с картой

1. Создай шаблон страницы

Файл: template-projects-map.php (в корне темы)

<?php
/**
 * Template Name: Карта проектов
 */

get_header();
?>

<div class="projects-map-page">
    <?php
    // Вывод заголовка и описания из ACF
    $header = get_field('header');
    $description = get_field('description');

    if ($header) {
        echo '<h1>' . esc_html($header) . '</h1>';
    }

    if ($description) {
        echo '<div class="projects-map-description">' . wp_kses_post($description) . '</div>';
    }

    // Вывод карты
    if (function_exists('render_projects_map')) {
        render_projects_map();
    }
    ?>
</div>

<?php
get_footer();

2. Создай страницу в WordPress

  1. Перейди в СтраницыДобавить новую
  2. Название: “Реализованные проекты”
  3. Выбери шаблон: Карта проектов
  4. Заполни поля ACF (заголовок, описание)
  5. Опубликуй страницу

3. Добавь проекты

  1. Перейди в ПроектыДобавить новый
  2. Заполни обязательные поля:
    • Название проекта: например, “Крематор КД-300”
    • Широта: 55.751574
    • Долгота: 37.573856
    • Адрес: “г. Москва, ул. Красная площадь, д. 1”
    • Тип оборудования: “Крематор”
    • Модель оборудования: “КД-300”
  3. Опубликуй проект

Перенос модуля в другой сайт

Модуль легко переносится в другой сайт:

  1. Скопируй папку модуля

    cp -r projects /path/to/new/theme/inc/modules/
  2. Подключи модуль в functions.php

    require_once get_template_directory() . '/inc/modules/projects/projects-loader.php';
  3. Установи ACF (если ещё не установлен)

  4. Настрой API ключ

    • Получи ключ на developer.tech.yandex.ru
    • Установи в настройках темы: Настройки контентаНастройки Яндекс.Карт
  5. Создай шаблон страницы (см. раздел “Использование”)

  6. Создай страницу с картой и добавь проекты


Кастомизация

Изменение внешнего вида карты

Файл: assets/css/projects-map.css

.projects-map__container {
  width: 100%;
  height: 600px;
  margin: 20px 0;
}

.projects-map__map {
  width: 100%;
  height: 100%;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

Изменение настроек карты

Файл: assets/js/projects-map.js

// Изменить центр карты
center: [59.934280, 30.335098], // Санкт-Петербург

// Изменить масштаб
zoom: 10,

// Изменить иконки меток
preset: 'islands#redIcon'

Добавление дополнительных полей

  1. Добавь поле в ACF (projects-acf.php)
  2. Добавь поле в функцию get_projects_for_map() (projects.php)
  3. Используй поле в JavaScript (assets/js/projects-map.js)

Безопасность

Модуль включает базовые меры безопасности:

  • Проверка прав доступа — все функции проверяют права доступа
  • Экранирование данных — все выходные данные экранируются через esc_html(), esc_attr(), wp_kses_post()
  • Валидация координат — проверка корректности координат перед добавлением на карту
  • Защита от XSS — экранирование HTML в JavaScript через функцию escapeHtml()

Частые проблемы и решения

Проблема: Карта не отображается

  • Решение: Проверь API ключ Яндекс.Карт, проверь консоль браузера на ошибки

Проблема: Метки не появляются на карте

  • Решение: Убедись, что проекты опубликованы и имеют координаты

Проблема: Поля ACF не отображаются

  • Решение: Убедись, что ACF установлен и активирован

Проблема: Скрипты не подключаются

  • Решение: Проверь, что страница использует шаблон template-projects-map.php

Итог

Модуль проектов для WordPress — это готовое решение для отображения локаций на карте:

  • ✅ Кастомный тип записи для управления проектами
  • ✅ ACF поля для координат и данных
  • ✅ Интеграция с Яндекс.Картами с кластеризацией меток
  • ✅ Адаптивный дизайн
  • ✅ Fallback данные для демонстрации
  • ✅ Легкий перенос в другой сайт

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


Ссылки

0 просмотров

Комментарии

Загрузка комментариев...
Пока нет комментариев. Будьте первым!