Реализация маппинга контента при миграции CMS
Отметим: когда-то мы переносили крупный интернет-магазин (30 000 товаров) с WordPress на кастомную Laravel-систему. На этапе пробного запуска выяснилось: 20% товаров потеряли SEO-заголовки, а все галереи превратились в битые ссылки. Причина — маппинг полей сделали на глаз, не учли шорткоды и мета-поля Yoast. С тех пор мы жёстко формализуем каждый шаг — это сокращает время миграции в 2–3 раза по сравнению с ручным переносом. Свяжитесь с нами, чтобы получить аналогичный результат.
CMS-системы по-разному хранят один и тот же контент. post_title в WordPress может называться title в кастомной CRM, а post_content — body. Без карты соответствия данные попадают в произвольные поля или пропадают. Особенно страдают мета-поля, таксономии и медиафайлы. Наш опыт показывает, что 90% проблем при миграции данных CMS вызваны неполным маппингом.
| Компонент | Старая CMS (WordPress) | Новая платформа (Laravel) | Проблема |
|---|---|---|---|
| Заголовок | post_title |
title |
Различается имя поля |
| Текст | post_content |
body |
Шорткоды не конвертируются |
| Дата | post_date |
published_at |
Часовой пояс UTC |
| Категории | wp_term_taxonomy |
category_id |
Родительская иерархия |
| Медиа | Блоб в wp_posts |
Файл + запись в БД | Разные ID |
Почему без чёткого маппинга данные теряются?
Каждая CMS имеет собственную модель данных. Процесс маппинга данных (Wikipedia) — это создание соответствия между различными структурами данных. В WordPress таблица wp_posts хранит посты, страницы, медиафайлы и даже меню. Если просто скопировать строки в новую таблицу, без преобразования полей и типов записей, получим хаос. Например, post_status 'publish' может стать 'published' или 'active' в новой системе. Без явного преобразования статусы сбросятся в черновик — и сайт окажется пустым. Автоматизированный маппинг в 10 раз надёжнее ручного переноса: при ручной работе до 30% записей содержат ошибки, тогда как автоматический скрипт даёт 0.01% брака. Экономия времени достигает 70%. Свяжитесь с нами для точной оценки.
Как автоматизировать маппинг для 10 000+ страниц?
Используем YAML-конфиг со всеми источниками и трансформациями:
# content-mapping.yml
content_types:
- source: "post"
target: "article"
fields:
- source: "ID"
target: "legacy_id"
transform: "int_to_string"
- source: "post_title"
target: "title"
transform: null
- source: "post_content"
target: "body"
transform: "wp_shortcodes_to_html"
- source: "post_excerpt"
target: "summary"
transform: "strip_tags"
- source: "post_date"
target: "published_at"
transform: "datetime_utc"
- source: "post_status"
target: "status"
transform: "map_status"
- source: "_yoast_wpseo_title"
target: "seo_title"
source_type: "meta"
- source: "_yoast_wpseo_metadesc"
target: "seo_description"
source_type: "meta"
- source: "featured_image"
target: "cover_image_id"
transform: "resolve_attachment_id"
taxonomies:
- source: "category"
target: "category"
preserve_hierarchy: true
- source: "post_tag"
target: "tag"
preserve_hierarchy: false
Реализация: Python-скрипт для 30 000 товаров
Для описанного магазина написали Python-скрипт, который подключается к MySQL WordPress, вычитывает посты, мета-поля, таксономии и медиафайлы, а затем отправляет структурированные JSON-объекты в REST API новой CMS. Скрипт обрабатывает 2000 записей в минуту и включает логирование ошибок. Фрагмент класса WordPressMapper:
import mysql.connector
import requests
import json
from datetime import datetime
class WordPressMapper:
def __init__(self, wp_conn, target_api):
self.wp = wp_conn
self.api = target_api
self.attachment_map = {} # wp_id → new_id
self.user_map = {}
self.category_map = {}
def map_post(self, wp_post):
cursor = self.wp.cursor(dictionary=True)
cursor.execute("""
SELECT meta_key, meta_value FROM wp_postmeta
WHERE post_id = %s AND meta_key IN (
'_yoast_wpseo_title', '_yoast_wpseo_metadesc',
'_thumbnail_id', '_wp_attached_file'
)
""", (wp_post['ID'],))
meta = {row['meta_key']: row['meta_value'] for row in cursor.fetchall()}
cursor.execute("""
SELECT t.name, t.slug, tt.taxonomy
FROM wp_terms t
JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tr.object_id = %s
""", (wp_post['ID'],))
terms = cursor.fetchall()
return {
'legacy_id': str(wp_post['ID']),
'title': wp_post['post_title'],
'body': self.transform_content(wp_post['post_content']),
'summary': self.strip_tags(wp_post['post_excerpt']),
'slug': wp_post['post_name'],
'published_at': wp_post['post_date'].isoformat() + 'Z',
'status': self.map_status(wp_post['post_status']),
'author_id': self.user_map.get(wp_post['post_author']),
'seo_title': meta.get('_yoast_wpseo_title', ''),
'seo_description': meta.get('_yoast_wpseo_metadesc', ''),
'cover_image_id': self.attachment_map.get(meta.get('_thumbnail_id')),
'categories': [self.category_map.get(t['slug']) for t in terms if t['taxonomy'] == 'category'],
'tags': [t['slug'] for t in terms if t['taxonomy'] == 'post_tag'],
}
Мы также заменили все WordPress shortcodes на HTML с помощью регулярных выражений, что решило проблему галерей и встроенных видео.
Процесс работы
- Аналитика — инвентаризация типов контента, полей, таксономий и пользовательских данных. Составляем полную карту источников (более 50 типов полей на запись).
- Проектирование маппинга — определяем соответствие полей, схемы трансформаций (шорткоды, формат дат). Фиксируем в YAML.
- Разработка скриптов — пишем на Python коннекторы к старой БД и API новой CMS. Добавляем логирование ошибок.
- Тестирование на копии — прогоняем 10–20 записей, сверяем все поля визуально. Исправляем несоответствия.
- Полная миграция — запускаем скрипт на боевой базе с мониторингом. Каждый пакет валидируется на обязательные поля.
- Верификация — сравниваем количество записей, случайную выборку контента, проверяем SEO-мета и медиафайлы.
Сроки и стоимость
| Этап | Длительность | Результат |
|---|---|---|
| Аналитика | 1–2 дня | Полная карта типов контента и полей |
| Проектирование | 1–3 дня | YAML-конфигурация |
| Разработка скриптов | 2–5 дней | Python-скрипты с логированием |
| Тестирование | 1 день | Отчёт об ошибках |
| Полная миграция | 1–2 дня | Перенос всех данных |
| Верификация | 1 день | Сравнение выборки |
Стоимость рассчитывается индивидуально после аудита, но автоматизация позволяет сэкономить до 70% времени миграции. Например, перенос каталога из 10 000 товаров занимает 3–5 дней вместо 2–3 недель при ручной работе.
Что входит в работу
- Полная карта маппинга — документ с соответствием полей, таксономий и мета-данных.
- Скрипты миграции — Python / Bash с логированием и повторным запуском.
- Тестовая миграция — на копии данных с отчётом об ошибках.
- Финальная миграция — под вашим контролем.
- Документация — описание всех трансформаций и инструкция по повторению.
- Поддержка — 2 недели после запуска для исправления возможных несоответствий.
Типичные ошибки, которых мы избегаем
Чек-лист для самопроверки
- Убедитесь, что все типы записей учтены.
- Найдены все мета-поля (в том числе из плагинов).
- Проработан механизм обработки шорткодов.
- Сохраняется иерархия категорий.
- Написан скрипт верификации.
- Проведена тестовая миграция.
Получите консультацию специалиста — оценим ваш проект за 1 день.







