Міграція сайту з однієї CMS на іншу
Міграція між CMS — це насамперед міграція даних. Відмінності в схемах, полях, типах контенту та медіафайлах потребують трансформації, а не простого копіювання.
Типові шляхи міграції
| Від | До | Складність |
|---|---|---|
| WordPress → Strapi | Середня | Схожі концепції CPT |
| WordPress → Contentful | Висока | Різні моделі даних |
| Drupal → Craft CMS | Висока | PHP↔PHP, але різні абстракції |
| Joomla → WordPress | Середня | Існують офіційні інструменти |
| Contentful → Sanity | Середня | Обидва headless, API-міграція |
| Ghost → WordPress | Низька | Ghost має XML-експорт |
Інструменти експорту
WordPress — вбудований XML-експорт або WP-CLI:
wp export --post_type=post,page,product --dir=/tmp/wp-export/
Drupal — Migrate API або CSV-експорт:
drush migrate-import --all
Contentful — CMA SDK:
contentful space export --space-id $SPACE_ID --export-dir ./backup
Ghost — Ghost Admin → Settings → Export → Export your content (JSON).
Strapi — власний скрипт через REST API.
Трансформаційний скрипт
Паттерн ETL (Extract → Transform → Load):
// scripts/cms-migration.ts
interface MigrationConfig {
source: 'wordpress' | 'contentful' | 'ghost';
target: 'strapi' | 'contentful' | 'sanity';
contentTypes: ContentTypeMapping[];
}
async function migrate(config: MigrationConfig) {
const extractor = getExtractor(config.source);
const transformer = getTransformer(config.source, config.target);
const loader = getLoader(config.target);
for (const mapping of config.contentTypes) {
console.log(`Мігрування: ${mapping.sourceName} → ${mapping.targetName}`);
// Extract
const items = await extractor.extract(mapping.sourceName);
// Transform
const transformed = items.map(item => transformer.transform(item, mapping));
// Load (з батчингом)
for (const batch of chunk(transformed, 50)) {
await loader.load(mapping.targetName, batch);
await delay(500);
}
}
}
Міграція Rich Text
Найскладніша частина — трансформація форматів Rich Text:
// WordPress HTML → Portable Text (Sanity)
import { htmlToPortableText } from '@portabletext/html';
function transformWpContent(html: string) {
return htmlToPortableText(html, {
rules: [
{
deserialize(el, next, block) {
if (el.tagName === 'IMG') {
return block({
_type: 'image',
_key: Math.random().toString(36).slice(2),
src: el.getAttribute('src'),
alt: el.getAttribute('alt'),
});
}
},
},
],
});
}
URL Redirect Mapping
Після міграції обов'язково встановіть редиректи 301:
// Генеруємо маппінг старих → нових URL
const redirects = oldPosts.map(old => ({
source: old.url,
destination: newPosts.find(n => n.slug === old.slug)?.url ?? '/blog',
permanent: true,
}));
// next.config.ts
async redirects() {
return redirects;
},
Міграція між двома headless CMS (1000–5000 записів) — 2–4 тижні.







