Настройка модуля Migrate для миграции контента в Drupal
Drupal Migrate — встроенная ETL-система (Extract, Transform, Load) для импорта данных из любых источников: старых версий Drupal, WordPress, CSV, XML, JSON, SQL-баз сторонних систем. Конфигурируется через YAML, расширяется плагинами.
Архитектура Migrate
Source — откуда читаем данные (CSV, SQL, JSON, Drupal 7 DB) Process — трансформация: маппинг полей, конвертация, обогащение Destination — куда записываем (Node, Term, User, File, Config)
Каждая миграция — отдельный YAML-файл в config/install/migrate_plus.migration.*.yml.
Установка
composer require drupal/migrate_plus drupal/migrate_tools drupal/migrate_source_csv
drush en migrate migrate_plus migrate_tools -y
Миграция из CSV
Файл config/install/migrate_plus.migration.articles_from_csv.yml:
id: articles_from_csv
label: 'Статьи из CSV'
migration_group: content_import
source:
plugin: csv
path: 'public://import/articles.csv'
ids:
- external_id
header_row_count: 1
column_names:
- external_id
- title
- body
- category
- publish_date
- image_url
process:
title: title
'body/value': body
'body/format':
plugin: default_value
default_value: full_html
created:
plugin: format_date
source: publish_date
from_format: 'd.m.Y'
to_format: 'U'
status:
plugin: default_value
default_value: 1
field_category:
plugin: migration_lookup
migration: categories_from_csv
source: category
field_image:
plugin: download
source:
- image_url
- '@filename'
destination:
plugin: 'public://images'
rename: true
destination:
plugin: 'entity:node'
default_bundle: article
migration_dependencies:
required:
- categories_from_csv
Кастомный Source плагин
Для нестандартных источников — кастомный плагин:
// src/Plugin/migrate/source/ExternalApiSource.php
namespace Drupal\mymodule\Plugin\migrate\source;
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
/**
* @MigrateSource(
* id = "external_api",
* source_module = "mymodule"
* )
*/
class ExternalApiSource extends SourcePluginBase {
public function getIds(): array {
return ['id' => ['type' => 'integer']];
}
public function fields(): array {
return [
'id' => 'ID записи',
'title' => 'Заголовок',
'content' => 'Содержимое',
'tags' => 'Теги (через запятую)',
];
}
protected function initializeIterator(): \Iterator {
$page = 0;
do {
$response = \Drupal::httpClient()->get(
'https://api.external.com/posts?page=' . $page,
['headers' => ['Authorization' => 'Bearer ' . $this->configuration['api_key']]]
);
$data = json_decode($response->getBody(), true);
$items = $data['items'];
foreach ($items as $item) {
yield $item;
}
$page++;
} while (!empty($items) && $page < $data['total_pages']);
}
}
Кастомный Process плагин
// src/Plugin/migrate/process/ExtractFirstImage.php
namespace Drupal\mymodule\Plugin\migrate\process;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\Row;
/**
* @MigrateProcessPlugin(id = "extract_first_image")
*/
class ExtractFirstImage extends ProcessPluginBase {
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property): ?string {
if (preg_match('/<img[^>]+src=["\']([^"\']+)["\']/', $value, $matches)) {
return $matches[1];
}
return NULL;
}
}
Запуск и мониторинг
# Список доступных миграций
drush migrate:status
# Запуск конкретной миграции
drush migrate:import articles_from_csv
# Запуск всех из группы
drush migrate:import --group=content_import
# С обновлением уже мигрированных записей
drush migrate:import articles_from_csv --update
# Откат миграции
drush migrate:rollback articles_from_csv
# Статус с прогрессом
drush migrate:status --format=table
Обработка ошибок
# Просмотр ошибок миграции
drush migrate:messages articles_from_csv
# Пропуск записей с ошибками и продолжение
drush migrate:import articles_from_csv --continue-on-failure
Миграция медиафайлов
# Шаг 1: миграция файлов
id: files_migration
source:
plugin: csv
path: 'public://import/files.csv'
process:
filename:
plugin: callback
callable: basename
source: file_url
uri:
plugin: download
source:
- file_url
- '@filename'
destination:
plugin: 'public://migrated'
destination:
plugin: 'entity:file'
# Шаг 2: в миграции нод
field_image:
plugin: migration_lookup
migration: files_migration
source: file_id
Incremental migration
Чтобы при повторном запуске мигрировались только новые записи, источник должен отслеживать highwater mark:
source:
plugin: csv
path: 'public://import/articles.csv'
ids:
- external_id
track_changes: true # перемигрировать при изменении строки
# Или через highwater mark (для дат)
highwaterProperty:
name: updated_at
alias: u
Сроки
Простая миграция из CSV (500–5000 записей) — 2–3 дня. Сложная миграция из нескольких источников с кастомными плагинами и трансформациями — 1–2 недели.







