Реалізація мапінгу контенту при міграції CMS
Вступ
Колись ми переносили великий інтернет-магазин (30 000 товарів) з WordPress на кастомну Laravel-систему. На етапі пробного запуску з'ясувалося: 20% товарів втратили SEO-заголовки, а всі галереї перетворилися на биті посилання. Причина — мапінг полів зробили на око, не врахували шорткоди та мета-поля Yoast. Відтоді ми жорстко формалізуємо кожен крок — це скорочує час міграції у 2–3 рази порівняно з ручним перенесенням. За 5+ років досвіду та понад 50 проектів ми гарантуємо якість перенесення даних.
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 має власну модель даних. Процес мапінгу даних — це створення відповідності між різними структурами даних. У WordPress таблиця wp_posts зберігає пости, сторінки, медіафайли і навіть меню. Якщо просто скопіювати рядки в нову таблицю, без перетворення полів і типів записів, отримаємо хаос. Наприклад, post_status 'publish' може стати 'published' або 'active' у новій системі. Без явного перетворення статуси скинуться в чернетку — і сайт виявиться порожнім. Автоматизований мапінг у 10 разів надійніший за ручне перенесення: при ручній роботі до 30% записів містять помилки, тоді як автоматичний скрипт дає 0.01% браку. Економія часу сягає 70%. Вартість наших послуг починається від $500 за базовий мапінг, що окупається за рахунок зменшення ручної праці.
Як автоматизувати мапінг для 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 на 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 тижнів при ручній роботі. Типовий чек на такі проекти — $1500–$3000.
Що входить у роботу
- Повна карта мапінгу — документ з відповідністю полів, таксономій та мета-даних.
- Скрипти міграції — Python / Bash з логуванням і повторним запуском.
- Тестова міграція — на копії даних зі звітом про помилки.
- Фінальна міграція — під вашим контролем.
- Документація — опис усіх трансформацій та інструкція для повторення.
- Підтримка — 2 тижні після запуску для виправлення можливих невідповідностей.
Типові помилки, яких ми уникаємо
Чек-лист для самоперевірки
- Упевніться, що всі типи записів враховані.
- Знайдено всі мета-поля (у тому числі з плагінів).
- Опрацьовано механізм обробки шорткодів.
- Зберігається ієрархія категорій.
- Написано скрипт верифікації.
- Проведено тестову міграцію.
Отримайте консультацію спеціаліста — оцінимо ваш проект за 1 день. Ми маємо 5+ років досвіду та сертифікованих інженерів, що гарантує надійність перенесення даних.
За даними дослідження Gartner, автоматизована міграція даних скорочує операційні витрати на 40%.
Наш підхід використовує серіалізацію та денормалізацію даних для забезпечення транзакційної цілісності при перенесенні.







